ข้ามไปยังเนื้อหา

The Pattern Language

ถึงตอนนี้คุณตัดสินใจได้แล้วว่า ควรหรือไม่ควร ใช้ microservices ถ้าคำตอบคือใช่ สิ่งที่รออยู่คือรายการ pattern ยาวเหยียด ทั้งกลยุทธ์ decomposition, saga, gateway, circuit breaker, tracing และอีกมาก จนน่าเข้าใจผิดว่าเป็นเมนูลูกเล่นที่หยิบใช้แยกกันได้ แต่ความจริงไม่ใช่ ทุก pattern มีอยู่เพราะการตัดสินใจก่อนหน้าสร้างปัญหาที่ pattern นั้นเข้ามาแก้ มองแบบนี้แล้ว pattern ทั้งหมดจะรวมกันเป็น ภาษา ที่เชื่อมโยงกัน และการเข้าใจความเชื่อมโยงนั้นมีค่ากว่าการท่องรายการชื่อ pattern มาก

คุณจะทำความเข้าใจ pattern หลายสิบตัวได้อย่างไรโดยไม่จมไปเสียก่อน สิ่งที่ต้องมีคือแผนที่ ที่ช่วยให้เห็นว่าทำไม decomposition ถึงคุ้มค่าตั้งแต่แรก และการตัดสินใจครั้งเดียวนั้นกระเพื่อมออกไปเป็นตระกูลปัญหาที่คอร์สที่เหลือพูดถึงได้อย่างไร

เราต้องการสองสิ่ง: โมเดลสำหรับ ทำไมคุณถึงแยก ระบบ และโมเดลสำหรับ การแยกสร้างอะไรขึ้นมา ที่ pattern ทั้งหลายต้องแก้ตามมา

วิธีที่มีประโยชน์ในการคิดเรื่อง scaling คือคิดไปตามแกนอิสระสามแกน ซึ่งมักวาดเป็นลูกบาศก์ (cube):

  • แกน X — horizontal duplication รันสำเนาแอปพลิเคชันที่เหมือนกันหลายชุดหลัง load balancer นี่คือการ scale แบบคลาสสิกที่ง่ายที่สุด และใช้กับ monolith ได้ด้วย ช่วย scale throughput แต่ไม่ช่วย scale ความซับซ้อน เพราะทุกสำเนายังแบกทั้งระบบไว้เหมือนเดิม
  • แกน Y — functional decomposition แยกแอปพลิเคชันตาม สิ่งที่ระบบทำ ออกเป็น service ตาม business capability นี่คือแกนที่นิยามความเป็น microservices และช่วย scale การพัฒนา เพราะทีมกับ component วิวัฒน์แยกกันได้ ไม่ใช่แค่ scale traffic
  • แกน Z — data partitioning แยกตาม ข้อมูลชุดไหน ที่ request ไปแตะ แล้วส่งแต่ละ request ไปยัง partition หรือ shard ที่ถือข้อมูลนั้น code เหมือนกันทุกที่ ต่างกันแค่แต่ละ instance ดูแลข้อมูลคนละส่วน

แกน Y คือแกนที่คอร์สนี้พูดถึงเป็นหลัก เพราะ functional decomposition คือสิ่งที่เปลี่ยนแอปพลิเคชันหนึ่งให้กลายเป็น service จำนวนมาก — และการเลือกครั้งเดียวนั้นคือต้นตอของกลุ่มปัญหาอื่น ๆ ทั้งหมดด้านล่าง

flowchart LR
  Mono[Single Application] --> X[X-axis<br/>Run many identical copies]
  Mono --> Y[Y-axis<br/>Split by business capability]
  Mono --> Z[Z-axis<br/>Partition by data]
  Y --> Decomp[Functional decomposition<br/>= microservices]
scale cube — แกน scaling อิสระสามแกน โดยการ decompose ตามแกน Y นำไปสู่ microservices

ทันทีที่คุณ decompose ตามแกน Y โซ่ของปัญหาใหม่ก็โผล่ตามมา และปัญหาแต่ละกลุ่มมีตระกูล pattern ของตัวเองคอยแก้ ทั้งหมดเรียงต่อกันมาตามลำดับที่เป็นธรรมชาติ:

flowchart TB
  Decomp[Decomposition<br/>split into services] --> Data[Data Management<br/>consistency without a shared DB]
  Data --> Msg[Transactional Messaging<br/>publish events reliably]
  Msg --> Comm[Communication<br/>how services talk]
  Comm --> Rel[Reliability<br/>survive partial failure]
  Rel --> Obs[Observability<br/>see across services]
  Obs --> Deploy[Deployment and Cross-cutting<br/>ship, run, secure]
decomposition ไหลต่อเป็นโซ่ของกลุ่มปัญหา แต่ละกลุ่มมีตระกูล pattern ของตัวเองคอยแก้

อ่านโซ่นี้เป็นเรื่องราว:

  • Decomposition แยกระบบออกเป็น service — แต่ตอนนี้ service เหล่านั้นต้องรักษาข้อมูลให้สอดคล้องกัน
  • Data Management ฟื้นความสอดคล้องและความสามารถในการ query โดยไม่ต้องมีฐานข้อมูลที่ใช้ร่วมกัน — แต่ความสอดคล้องที่เชื่อถือได้ขึ้นอยู่กับการเผยแพร่ event ในฐานะส่วนหนึ่งของ transaction
  • Transactional Messaging ทำให้การเผยแพร่ event นั้นเชื่อถือได้ — แต่ service ก็ยังต้องคุยกันอยู่ดี
  • Communication กำหนดว่า service คุยกันอย่างไร ทั้งแบบ synchronous และ asynchronous ผ่าน API และ gateway แต่เครือข่ายที่ใช้คุยกันนั้นล้มเหลวได้แบบเป็นบางส่วนและคาดไม่ถึง
  • Reliability ทำให้ระบบยังทำงานต่อได้ผ่าน partial failure เหล่านั้น — แต่คุณแก้สิ่งที่คุณมองไม่เห็นข้ามกองทัพ service ไม่ได้
  • Observability ทำให้คุณมองเห็นภาพรวมข้ามทุก service ได้ และสุดท้ายทั้งหมดก็ต้อง ship รัน และรักษาความปลอดภัยบน production
  • Deployment and Cross-cutting จัดการช่วงสุดท้ายนั้น

ทุกลูกศรคือปัญหาที่ถูกส่งต่อไปข้างหน้า และนั่นคือเหตุผลที่สิ่งนี้เป็น ภาษา ไม่ใช่แค่รายการ คุณเข้าใจ saga ได้ไม่ครบถ้าไม่รู้จัก data boundary ที่ทำให้ต้องมี saga ตั้งแต่แรก และคุณรัน saga อย่างปลอดภัยไม่ได้ถ้าไม่มี messaging ที่เชื่อถือได้รองรับอยู่ข้างใต้ pattern ทั้งหลายอ้างอิงและพึ่งพากันเป็นทอด ๆ

พอมีแผนที่นี้ คุณก็วางตำแหน่งของ pattern แต่ละตัวได้ เจอ pattern ใหม่เมื่อไร ให้ถามสองข้อว่าอยู่ในกลุ่มไหน และการตัดสินใจก่อนหน้าข้อใดที่สร้างปัญหาซึ่ง pattern นี้เข้ามาแก้ นิสัยเล็ก ๆ ข้อนี้เปลี่ยนแคตตาล็อกที่กระจัดกระจายให้กลายเป็นโครงสร้างที่เดินตามได้ แถมยังบอกด้วยว่าควรเรียนอะไรต่อ เพราะแต่ละกลุ่มต่อยอดกันไปตามลำดับ

จุดเริ่มต้นที่เป็นธรรมชาติในการนำสิ่งนี้ไปใช้คือปัญหาแรกที่ decomposition สร้างขึ้น: การรักษาข้อมูลให้สอดคล้องเมื่อทุก service เป็นเจ้าของ store ของตัวเอง

  • What Is Microservices Architecture? — รากฐานที่ทั้งภาษาตั้งอยู่บน
  • When to Use Microservices — การตัดสินใจที่จะ decompose ซึ่งเริ่มต้นโซ่นี้ให้เคลื่อนไหว
  • Data Management — กลุ่มปัญหาแรกที่ decomposition สร้างขึ้น และเป็นโมดูลถัดไปที่ต้องอ่าน
ข้อดีข้อแลกเปลี่ยน
pattern ให้ vocabulary ร่วมกันในทีม — คุยกันเข้าใจเร็วขึ้นpattern เป็น template ไม่ใช่ recipe — ต้อง adapt ตาม context
แต่ละ pattern documented ว่า context ไหนเหมาะการรู้ pattern ไม่ได้แปลว่า implement ถูก
เรียนจาก collective experience ของอุตสาหกรรมover-engineering — ใช้ pattern ที่ยังไม่จำเป็น
pattern language ช่วย see the big picture ว่า pattern สัมพันธ์กันอย่างไรpattern อาจ outdated สำหรับ technology stack ปัจจุบัน

Pattern Shopping — เลือก pattern เพราะชื่อดังหรือ hype ไม่ใช่เพราะ context เหมาะ

อาการ:

  • ใช้ CQRS เพราะอ่านบทความ ไม่ใช่เพราะมีปัญหา read/write asymmetry
  • เพิ่ม complexity โดยไม่มี corresponding benefit
  • เริ่มจาก problem ก่อนเสมอ — pattern เป็นคำตอบไม่ใช่คำถาม

ใช้ Pattern โดยไม่เข้าใจ Trade-offs — implement pattern ตาม tutorial โดยไม่รู้ว่าจะเสียอะไร

อาการ:

  • adopt Saga โดยไม่รู้ว่า compensating transaction คืออะไร
  • adopt Event Sourcing โดยไม่รู้ว่า query current state ยากขึ้น

💡 ตัวอย่างจากของจริง

Chris Richardson (microservices.io):

  • สร้าง microservices pattern language catalog
  • patterns สัมพันธ์กัน — แก้ปัญหาหนึ่งนำไปสู่ปัญหาถัดไปที่ต้องใช้ pattern ใหม่

Martin Fowler & Sam Newman:

  • “Microservices” article (2014) นิยาม architectural style
  • “Building Microservices” book เป็น reference สำคัญของอุตสาหกรรม
แกนไหนของ scale cube ที่ตรงกับการแยกแอปพลิเคชันออกเป็น microservices
อะไรทำให้ pattern ของ microservices เป็น "ภาษา" ไม่ใช่แค่รายการ
ตามโซ่ของปัญหา decomposition สร้างกลุ่มปัญหาใดขึ้นมาเป็นอันดับแรก