When to Use Microservices
ตอนนี้คุณนิยาม microservices ได้แล้ว และท่อง trade-off ได้ด้วย ทีนี้ก็ถึงการตัดสินใจที่สำคัญจริงในโปรเจกต์จริง คือ ระบบนี้ควรเป็น microservices architecture หรือไม่ และถ้าควร ควรเมื่อไร ตรงนี้แหละที่วิจารณญาณทางวิศวกรรมจะออกฤทธิ์ เพราะตอบผิดไปทางไหนก็แพงทั้งนั้น decompose เร็วเกินไป คุณจะจ่ายต้นทุนของระบบแบบกระจายทั้งที่ยังไม่มี scale มารองรับเหตุผล decompose ช้าเกินไป คุณจะต้องสู้กับ monolith ที่กลายเป็นคอขวดของทีมใหญ่
ไม่มีกฎสากล มีแต่สัญญาณ ความผิดพลาดคือการตัดสินใจตามแฟชั่น แบบ “ตอนนี้ใคร ๆ ก็ใช้ microservices” หรือตัดสินใจเพราะกลัว แบบ “monolith ของเรารกมาก มันต้องผิดแน่ ๆ” ความจริงคือ monolith ที่รกมักเป็นสัญญาณว่าคุณต้องการเส้นแบ่งภายในที่สะอาดกว่าเดิม ไม่ใช่ต้องการเครือข่ายมาคั่นระหว่าง module
ดังนั้นคำถามคือ: ปัจจัยที่เป็นรูปธรรมข้อไหนควรผลักคุณไปสู่ microservices และข้อไหนควรรั้งคุณไว้กับ monolith?
วิธีแก้
หัวข้อที่มีชื่อว่า “วิธีแก้”ให้ชั่งน้ำหนักสามปัจจัยนี้พร้อมกัน ทั้งสามเสริมกัน และไม่มีปัจจัยไหนชี้ขาดได้ด้วยตัวเองลำพัง:
- ขนาดทีม นักพัฒนาเพียงหยิบมือ ship ได้เร็วที่สุดบน codebase เดียว เมื่อคุณมีหลายทีมแย่งชิง codebase เดียวและ deploy pipeline เดียว ต้นทุนการประสานงานก็พุ่งขึ้น และการให้แต่ละทีมมี service ที่ deploy ได้ของตัวเองสามารถลบการแย่งชิงนั้นออกไป
- ความซับซ้อนของ domain domain เล็ก ๆ ที่เข้าใจกันดีอยู่แล้วแทบไม่ต้องการการแยก domain ขนาดใหญ่ที่มีพื้นที่ย่อยชัดเจนซึ่งวิวัฒน์ในอัตราที่ต่างกันและมีขอบเขตตามธรรมชาติ คือผู้สมัครสำหรับการ decompose ไปตามขอบเขตเหล่านั้น
- ความต้องการ scale ถ้าทั้งระบบ scale ได้ดีอยู่แล้วในฐานะหน่วยเดียว ก็ไม่ต้องแยกเพื่อ scale แต่ถ้า component แต่ละตัวมีโปรไฟล์ load ต่างกันมาก เช่น ตัวหนึ่ง CPU-bound อีกตัว memory-bound อีกตัวรับ traffic มากกว่าสิบเท่า การ scale แยกอิสระก็กลายเป็นเหตุผลที่หนักแน่นพอจะแยกออกจากกัน
decision flow ด้านล่างพาเดินผ่านสัญญาณเหล่านี้ตามลำดับ สังเกตว่ามีกี่เส้นทางที่จบลงที่ “monolith” นั่นเป็นความตั้งใจ
flowchart TB
Start([New or growing system]) --> Q1{Large team blocked<br/>on one codebase or<br/>one pipeline?}
Q1 -- No --> Mono[Build / keep a<br/>modular monolith]
Q1 -- Yes --> Q2{Domain has clear,<br/>independent<br/>sub-areas?}
Q2 -- No --> Mono
Q2 -- Yes --> Q3{Components have<br/>very different<br/>scaling needs?}
Q3 -- No --> Q4{Operational maturity:<br/>CI/CD, automation,<br/>observability in place?}
Q3 -- Yes --> Q4
Q4 -- No --> Mono2[Invest in automation<br/>first; decompose later]
Q4 -- Yes --> Micro[Decompose into<br/>microservices] Monolith first
หัวข้อที่มีชื่อว่า “Monolith first”ตรงนี้มีค่าเริ่มต้นที่หนักแน่นอยู่ มักเรียกกันว่า monolith first คือเริ่มจาก monolith ที่มีโครงสร้างดี ทำผลิตภัณฑ์ให้ถูกก่อน แล้วค่อย ๆ เรียนรู้ว่าเส้นแบ่งที่แท้จริงใน domain ของคุณอยู่ตรงไหน เส้นแบ่งของผลิตภัณฑ์ที่ยังใหม่เป็นแค่การเดา และถ้าเดาผิด การแก้ภายใน codebase เดียวถูกกว่าการแก้ข้ามเครือข่ายของ service มาก พอระบบโตขึ้นและคุณเห็นความเจ็บปวดที่แท้จริง เช่น ทีมติดอยู่กับ deploy ร่วม หรือ component ที่ต้อง scale แยก ค่อยดึงชิ้นนั้นออกมาเป็น service สรุปคือคุณ decompose ตามเส้นแบ่งที่ ค้นพบ ไม่ใช่เส้นแบ่งที่ จินตนาการ เอาไว้ตั้งแต่วันแรก
นี่ไม่ได้หมายความว่า monolith แบบลวก ๆ ทรัพย์สินที่ทำให้การดึงออกในภายหลังราคาถูกคือ modular monolith: monolith ที่มีขอบเขต module ภายในที่สะอาด ทำให้การดึง module ออกมาเป็น service เป็น mechanical refactor มากกว่าการแกะปมที่พันกันยุ่ง
ผลลัพธ์ที่ตามมา
หัวข้อที่มีชื่อว่า “ผลลัพธ์ที่ตามมา”เมื่อสามปัจจัยมาบรรจบกัน คือองค์กรขนาดใหญ่ domain ที่ซับซ้อนและมีรอยต่อตามธรรมชาติ และ component ที่มีความต้องการ scale ต่างกัน และ คุณมีวุฒิภาวะด้านปฏิบัติการพอจะรัน service จำนวนมาก microservices ก็มีแนวโน้มจะคุ้ม แต่ถ้าสามปัจจัยไม่มาพร้อมกัน modular monolith แทบจะเป็นตัวเลือกที่ดีกว่า ถูกกว่า และเร็วกว่าเสมอ แถมยังเปิดทางให้ decompose ทีหลังได้อยู่
ความจริงที่ช่วยให้อุ่นใจคือ นี่ไม่ใช่การตัดสินใจแบบทั้งหมดหรือไม่เอาเลยที่ย้อนกลับไม่ได้ คุณเริ่มจาก monolith ก่อนแล้วค่อยลอก service ออกมาทีละตัวเมื่อความจำเป็นเกิดขึ้นจริงได้ ดึง component ที่เจ็บที่สุดออกก่อน แล้วปล่อยที่เหลือไว้ใน monolith ตราบใดที่ยังใช้งานได้ดีอยู่
pattern ที่เกี่ยวข้อง
หัวข้อที่มีชื่อว่า “pattern ที่เกี่ยวข้อง”- Benefits and Drawbacks — บัญชีที่การตัดสินใจนี้ชั่งน้ำหนัก
- Monolith vs Microservices — สองตัวเลือกที่ flow เลือกระหว่างกัน
- The Pattern Language — สิ่งที่ต้องเรียนรู้เมื่อคุณตัดสินใจที่จะ decompose แล้ว
| ใช้ Microservices เมื่อ | อย่าใช้เมื่อ |
|---|---|
| ทีมโตพอ (8+ คน) แบ่งเป็น squad ได้ | ทีมเล็กกว่า 5 คน |
| domain ชัดเจน — รู้ boundary ของแต่ละ service | ยังไม่รู้ domain ดีพอ — boundary เปลี่ยนบ่อย |
| มี DevOps maturity — CI/CD, monitoring, K8s | ยังไม่มี DevOps infrastructure |
| scale requirement ต่างกันระหว่าง service | traffic ทุก service scale ใกล้เคียงกัน |
| compliance ต้องการ isolate ข้อมูล | ต้องการ strong consistency ทั้งระบบ |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”Microservices เป็น Default Architecture — เริ่ม project ใหม่ด้วย microservices ทุกครั้ง
อาการ:
- sprint แรกเสียไปกับ setup K8s, service registry, CI/CD pipeline
- feature แรกยังไม่ได้ทำ
- ใช้ Monolith หรือ Modular Monolith แล้ว extract เมื่อถึงเวลา
ย้ายเพราะ Competitor ใช้ — adopt microservices เพราะ Netflix/Amazon ใช้
อาการ:
- ignore context ที่แตกต่าง — Netflix มีทีม thousands คน
- complexity ไม่คุ้มกับ scale ที่มี
- “ถ้า Netflix ทำได้ เราก็ทำได้” — แต่ Netflix ใช้เวลา 7 ปี migrate
💡 ตัวอย่างจากของจริง
Basecamp / Hey:
- ใช้ Rails monolith ตลอด — scale ได้ดีสำหรับ business ขนาดนั้น
- DHH (creator ของ Rails) active advocate ของ “Majestic Monolith”
Shopify:
- เริ่มจาก Rails monolith
- ย้ายไป Modular Monolith ก่อน (Storefront Renderer)
- extract microservice เฉพาะเมื่อ module พร้อมและ team พร้อม