จากบทความเรื่อง
SHIFT Commerce’s Journey: Deconstructing Monolithic Applications into Services
ทำการอธิบายขั้นตอนการเปลี่ยนสถาปัตยกรรมระบบจาก Monolith ไปเป็น Microservices
เป็นกรณีศึกษาที่น่าสนใจ จึงทำการสรุปไว้นิดหน่อย

เหตุผลสำหรับการนำ Microservices มาใช้

1. เรื่องของการพูดคุยที่เยอะเกินไป
ในการพัฒนาระบบนั้น พบว่า
เมื่อต้องเริ่มต้นพัฒนา
เมื่อต้องเพิ่ม feature เข้าไปยังระบบ
เมื่อต้องแก้ไขระบบ
สิ่งที่เกิดขึ้นคือ ต้องพูดคุย ประชุมกับหลายทีมมากมาย
ดังนั้นกว่าจะได้เริ่มพัฒนา ต้องใช้เวลาสูงมาก
ยิ่งเมื่อพัฒนาปัญหาก็เยอะมาก
ปรับเปลี่ยนสิ่งใด จะยากมาก ๆ

2. การ deploy ระบบงานทุกครั้ง ใช้เวลา และ มีความเสี่ยงสูงทุกครั้ง
เมื่อเกิดความกลัวแล้ว สิ่งที่ตามมาคือ ความระมัดระวัง
ทั้งการเพิ่มขั้นตอนที่มากมาย
ทั้งเวลาที่ใช้เยอะมาก ๆ
ผลที่ตามมาคือ ระบบไม่สามารถใช้งานได้
นั่นคือผลกระทบต่อผู้ใช้และธุรกิจอีกด้วย

3. เจอข้อจำกัดของ infrastructure และภาษาโปรแกรม
ยิ่งระบบมีขนาดใหญ่ขึ้น ปัญหาที่ตามมาคือ
infrastructure ที่มีอยู่ไม่สามารถรองรับได้หรือถึงขีดจำกัด
สิ่งที่ต้องทำคือ ขยายนั่นเอง
ทำให้ต้องใช้ค่าใช้จ่ายที่สูงมาก ๆ
รวมไปถึงภาษาโปรแกรมหรือเทคโนโลยีที่ใช้งานอีกด้วย
เช่น ถ้าระบบเดิมคือภาษา Ruby นั้น
เริ่มรองรับจำนวนผู้ใช้งานได้ช้าลง
การจะเปลี่ยนไปใช้ภาษา Go หรือ NodeJS น่าจะดีกว่า
แต่ก็ทำไม่ได้ หรือ ยากมาก ๆ

จากปัญหาต่าง ๆ เหล่านี้
จึงทำให้ทางทีมพัฒนานั้น เริ่มคิดและหาวิธีแก้ไข
หนึ่งในนั้นคือ การแบ่งระบบงานใหญ่ ๆ
ออกมาเป็นระบบหรือ service ย่อย ๆ
ที่เราเรียกกันว่า Microservice นั่นเอง

ปล. ถ้ายังไม่มีปัญหาเหล่านี้ ก็ให้พิจารณากันหน่อยนะ

มาดูวิธีการแก้ไขปัญหากันดูว่าเป็นอย่างไรบ้าง ?

แน่นอนว่า เริ่มด้วยการแยก service บางตัวออกมาก่อน
แต่การแยกเป็น service ย่อย ๆ ทั้งระบบเลยก็ไม่ได้
เพราะว่า จะมีปัญหาต่าง ๆ มากมายให้แก้ไข ซึ่งนรกมาก ๆ
ยังไม่พอนะ ปัญหาที่ตามมาคือ

เมื่อแยก service ออกมาแล้ว
การติดต่อสื่อสารระหว่างระบบเดิมกับ service เล็ก ๆ ต้องทำอย่างไร ?
ในระบบนี้มีความต้องการดังนี้
ทำงานแบบ real time
ดังนั้นถ้ามีข้อมูลเปลี่ยนแปลง ส่วนต่าง ๆ ที่เกี่ยวข้องต้องทำงานด้วย
แต่ไม่ต้องการให้แต่ละส่วนงานต่าง ๆ ยึดติดกันเกินไป (Decouple หรือ Loose coupling)
จึงเลือกใช้งานการติดต่อกันแบบ Asynchronous

แน่นอนว่าต้องมีคนกลางสำหรับการติดต่อกัน (Messaging)
คำถามคือ ต้องใช้อะไรเป็นตัวกลางในการติดต่อกัน
เช่น ActiveMQ, RabbitMQ และ Apache Kafka
ทางทีมพัฒนานั้นเลือกใช้ Apache Kafka
(ในบทความเป็น service บน Heruku นะ เพื่อลดปัญหาในการดูแลรักษาระบบเอง)

มาดูขั้นตอนการย้ายระบบกันบ้าง

ทำการย้ายแบบค่อยเป็นค่อยไป
ไม่ทำแบบ big bang เนื่องจากมีความเสี่ยงสูงมาก ๆ
โดยประกอบไปด้วย 8 ขั้นตอนดังนี้

ขั้นตอนที่ 1 สร้าง Producer logic ในฝั่งของระบบเดิม
เริ่มด้วยการเพิ่ม code ในระบบเดิม
ทำหน้าที่ส่ง event หรือการเปลี่ยนแปลงต่าง ๆ ไปยังตัวกลางคือ Apache Kafka

ขั้นตอนที่ 2 สร้าง Consumer ในส่วนของ service ใหม่
ทำการดึงข้อมูลหรือ event จาก Apache Kafka มาจะเก็บใน Database ของ service ก่อน
เพื่อจะนำมาใช้งานใน service นี้ต่อไป
เป็นขั้นตอนแรกของการแยก service ออกมา แสดงดังรูป

ขั้นตอนที่ 3 ทดสอบว่า Consumer ทำงานได้ดีและถูกต้องหรือไม่
ต้องทำให้แน่ใจและมั่นใจว่า
Consumer ทำงานได้อย่างถูกต้อง
ทั้ง event ต่าง ๆ และการบันทึกข้อมูลลง Database
รวมทั้งทำความเข้าใจกับการทำงานในรูปแบบต่าง ๆ ของ Apache Kafka อีกด้วย
เมื่อทุกอย่างเข้าที่เข้าทาง ก็ไปต่อกัน

ขั้นตอนที่ 4 เริ่มสร้าง logic ในส่วนของ service ใหม่
นั่นคือการ copy หรือ duplicate logic การทำงานมาจากระบบเดิมนั่นเอง
จากนั้นทำการทดสอบว่า ระบบยังคงทำงานได้เช่นเดิมหรือไม่
แสดงดังรูป

ขั้นตอนที่ 5 เพิ่ม Logic เพื่อส่ง action event หรือการทำงานต่าง ๆ มายัง service
จากสิ่งที่ผ่านมา เพียงแค่ส่งข้อมูลที่เปลี่ยนแปลงมาเท่านั้น
สิ่งต่อมาคือ action event ของการทำงานนั่นเอง
และความสามารถที่ต้องเพิ่มมาคือ Feature Toggle ในฝั่งระบบเดิม
เพื่อให้เปิดปิดการส่งข้อมูลการทำงานส่วนต่าง ๆ มายัง service ใหม่ได้
แสดงดังรูป

ขั้นตอนที่ 6 เมื่อ Service ใหม่ทำงานตาม action event แล้ว ก็ต้องผลการทำงานกับไปยังระบบเดิม
สิ่งที่ต้องสร้างคือ
Producer ในฝั่ง service ใหม่
Consumer ในฝั่งระบบเดิม
แสดงดังรูป

ขั้นตอนที่ 7 ทดสอบ ทดสอบ ทดสอบ
ทั้งการเปิดปิดจาก Feature Toggle
เพื่อทำให้มั่นใจว่า ระบบยังคงทำงานได้เช่นเดิม (ยากมาก ๆ)

ขั้นตอนที่ 8 เมื่อมั่นใจก็ลบ logic หรือส่วนการทำงานออกจากระบบเดิม
แน่นอนว่าต้องเปิด Feature Toggle ให้ไปใช้ logic ที่ service ใหม่ทั้งหมด
ต่อไปในอนาคตอันใกล้ ก็ให้ลบ Feature Toggle ออกไป
เนื่องจากการทำงานหลักจะย้ายไปที่ service ใหม่แล้ว
นี่คือขั้นตอนการลบสิ่งที่ซ้ำซ้อนนั่นเอง

เมื่อทำงานครบทั้ง 8 ขั้นตอน
ก็จะทำให้เราสามารถแยก service ออกมาจากระบบเดิมได้แบบปลอดภัยแล้วนะ

โดยสิ่งที่ทางทีมพัฒนาได้รับจากการเปลี่ยนแปลงประกอบไปด้วย

  • Deploy และ Release ได้บ่อยขึ้น
  • ลดปัญหาเรื่อง I/O Latency จากการเรียกผ่าน API
  • ความพึงพอใจของผู้ใช้งานเพิ่มขึ้น

คำถามที่น่าสนใจ ทำไมเราถึงนำแนวคิดของ Microservices มาใช้งาน ?