Refactoring Monolith to Microservices by using Strangler Pattern
ผมค่อนข้างมั่นใจว่าหลายคนที่เข้ามาอ่าน หน้าจะรู้จัก หรือเคยได้มีโอกาสพัฒนา Project ที่มีสถาปัตยกรรม ทั้งแบบ Monolith
และแบบ Microservices
กันมาอยู่แล้ว ขึ้นอยู่ว่าจะมาก หรือจะน้อยอย่างไร
บทความนี้จะไม่ขอพูดถึงความแตกต่างของมัน แต่เราจะมาพูดถึงวิธีการ Refactor จาก Monolith
ไปสู่ Microservices
ด้วยวิธี Strangler Pattern
กัน
Strangler Pattern
Strangler Pattern เป็นวิธีการ Migrate จากระบบเดิม โดยแทนที่ฟังก์ชันการทำงานที่มีอยู่เดิมด้วย Application และ Services ใหม่ แบบค่อยเป็นค่อยไป หลังจากการแทนที่ฟังก์ชันการทำงานทั้งหมดแล้ว ระบบใหม่จะแทนที่คุณลักษณะของระบบเดิมทั้งหมดในที่สุด
ชื่อ “Strangler Pattern” ได้รับแรงบันดาลใจจากต้นมะเดื่อ
Understand your system
สิ่งแรกที่เราต้องรู้ และควรจะต้องมีเลย คือ ความเข้าใจในตัวระบบของตนเองก่อน ต้องทราบว่าระบบของเรามีการทำงานอย่างไร มีกี่ Component มีการ Connect ไปที่ไหนบ้าง มีใครเชื่อมต่อเข้ามาบ้าง ถ้าระบบที่เป็น Monolith เดิมมี Unit Test ด้วยก็จะช่วยเราได้อย่างมากมาย หรืออย่างน้อยมี E2E Test ก็ยังดี ที่สำคัญควรจัดทำเอกสารให้ชัดเจนทั้ง (AS IS) และ (TO BE)
Refactoring strategies
- Implement ฟังก์ชันใหม่ As services
- แตก Service ออกจาก Monolith
Implement new functionality as services
ตรงนี้เป็นจุดเริ่มต้นที่ดีในการที่เราจะ Migrate monolith ไปสู่ Microservices การที่เราได้รับ Requirement ใหม่ ๆ มาให้เราไปทำที่ Service ใหม่เลย ตรงนี้มันจะง่ายกว่าการแยก Service เดิมออกจาก Monolith
เมื่อใดก็ตามที่ได้รับ Requirement เข้ามาใหม่ ให้เรา Implement มัน As a service อย่าไปเติมของที่ Monolith
Extract services from Monolith
เราจะขอยกตัวอย่างซักระบบนึง ที่เป็น Monolith
ที่เราต้องการจะ Improve มัน โดยให้ชื่อมันว่า Gique System
ซึ่งในนั้นมีระบบต่าง ๆ มากมาย แต่จะขอยกออกมา 2 ตัวด้วยกันคือ Order Management
และ Trade Management
โดยเราจะทำการแยก Order
และ Trade Management
ออกจากกัน
แตก Trade management service
ออกมา
- แยกโค้ดในส่วนของ
Trade management
ออกจากกัน ภายในMonolith
- แยก Database และสร้าง Schema สำหรับ
Trade management
- กำหนด และใช้
Standalone Trade Management service
ในMicroservices
- ลบ Code เก่าที่ไม่ได้ใช้แล้วใน
Function
ที่เกี่ยวข้องกับTrade management
ที่อยู่ในMonolith
ออกไป
Let’s Go
Review AS IS
ใน Gique System ที่เดิมทีเป็น Monolith service ดังภาพด้านล่างนี้
นี่เป็นเพียงภาพตัวอย่างคร่าว ๆ นะครับ ถ้าสงสัย เชื่อว่าสงสัยแหละ อย่าสงสัยเยอะ มันหลวม ๆ เท่านั้น
Split code
เริ่มต้นด้วยทำการแบ่งแยก Code และ Convert ตัว Trade Management
ออกเป็น Module ให้มันมี Dependency กันน้อย ๆ หลวม ๆ ภายใน Monolith
ของเรา
อาจจะใช้ท่า Interface ก็ได้ในการคุยกันระหว่าง Order
กับ Trade
เพื่อให้เกิด Loosely coupled โดยที่ Trade
เป็น Services ใหม่ที่อยู่ใน Monolith
ที่สำคัญพอแยกแล้วมันทำให้เราเขียน Test ได้ด้วย (ในกรณีที่ของเดิมมันไม่มี Test) ถ้าของเดิมมี Test อยู่แล้วยิ่งดีครับ ได้ใช้ TDD กันสนุกมือ
Split Database
ทำการ Split database
และทำการกำหนด Schema
ขึ้นมาแยกต่างหาก สำหรับ Trade management
ของเรา
ระหว่างการแยกต้องคำนึงถึงพวกเรื่อง Trigger ระหว่าง 2 DB ด้วยระหว่างที่ยัง Implement และที่สำคัญอย่าลืมเรื่อง Migration ด้วยนะครับ
Define Standalone Service
สร้าง Standalone service
ขึ้นมา ซึ่งนั่นก็คือ Trade Management Service
นั่นเองจากนั้นลองเอามันขึ้นไปใช้ดูก่อนว่ามันสามารถใช้งานได้อย่างถูกต้องมั้ย แบบคร่าว ๆ ถ้าดูจากรูปจะเห็นว่า มันแค่เกิด Services ใหม่ขึ้นมา ยังไม่ได้ใช้จริงนะ คือมีเอาไว้ใช้เทสก่อน ยิงเข้ามาได้ แต่อาจจะไม่ได้เปิดให้ ลูกค้าจริง ๆ ใช้งาน
Use Standalone Service
มาถึงตรงนี้ก็เกือบจะสุดท้ายแล้ว คือข้างบนมัน Test เสร็จหมด มั่นใจสุด ๆ แล้วแหละ คือการไปใช้ Standalone service
ที่เราสร้างขึ้นมาใหม่ นั่นก็คือ Trade management service
นั่นเอง โดยเราก็จะทำการใช้วิธีสร้าง API Gateway ขึ้นมาเพื่อทำ Single Entry Point กับ API Management ให้เรา โดยที่ เมื่อ User request มาที่เกี่ยวกับ Trade ตัว API Gateway ก็จะทำการพา User ไปที่ Trade management service
ถ้าอยากได้ข้อมูลเพิ่มเติมเชิญไปอ่านกันได้ที่นี่ครับ
Remove Trade function from Gique System
ขั้นตอนสุดท้ายแล้ว! ทำการ ลบ Function ใด ๆ ที่เกี่ยวข้องกับ Trade ที่อยู่ใน Monolith ออกจากระบบให้หมด ไม่ว่าจะเป็น Controller, Services, หรือ Repositories ไม่ต้องกล้วพัง ถ้าพัง มันพังไปนานแล้ว จากข้อข้างบน ๆ หรือไม่ก็ลบเกิน…
จะเห็นว่า Monolith เดิมก็ยังอยู่ เพียงแต่เราถอด Trade ออกไป ไม่ได้หมายความว่า Orders ของเราจะกลายเป็น Microservices นอกเสียจากว่าทั้งระบบที่เหลือ มันเหลือแค่ Orders อย่างเดียวจริง ๆ อันนี้ขึ้นอยู่กับภายในแล้วว่า ข้างในมันยังมีอะไรอีกมั้ย หรือมันจะแตกออะไรออกมาได้อีกมั้ย
ซึ่งถ้าอยากจะถอดเพิ่มอีก ก็วนทำตั้งแต่ ข้อแรกใหม่ วนไปเรื่อย ๆ จนคิดว่าน่าจะเพียงพอแล้ว หรือจะใช้หลัก Domain Driven ช่วยก็ได้นะครับ
Conclusions
หลัก ๆ ก็จะมีกันอยู่ประมาณไม่กี่ Steps ในการถอด Monolith ออกมาเป็น Microservices ด้วยการใช้ Strangler Pattern อาจจะดูว่าโอ้ว แค่นี้เอง แต่แท้จริงแล้ว การจะถอดออกก็ไม่ใช่เรื่องง่ายเสียทีเดียว เนื่องจากระบบแต่ละระบบ ก็จะมี Dependency ที่แตกต่างกันออกไป ซึ่งก็จะใช้เวลาในการ Implement ที่ไม่เท่ากัน ในบทความก็ไม่ได้กล่าวแบบละเอียดยิบ ๆ บรรทัดต่อบรรทัด
อย่าลืมว่า ไม่ใช่ว่าทุกอย่างเราจะจำเป็นต้อง Microservices นะครับ เริ่มต้นบางครั้ง เริ่มด้วย Monolith ก่อนก็ได้ อย่าไปยึดติดมากเกินไป บางครั้งเราก็ต้องการ Time to Market ก่อน แล้วค่อยมาเปลี่ยน เมื่อมันถึงเวลาต้องเปลี่ยนก็ได้
สิ่งที่สำคัญในการทำก็คือ เราต้องรู้ AS IS ก่อน หลังจากนั้น เราต้อง วาด Flow ออกมาให้ป็น TO BE ให้ได้ ค่อยลงมือทำ เนื่องจากถ้าเราไม่วางแผนก่อน อาจจะทำให้มันไปไม่ถึงฝั่งฝัน, อาจจะทำให้ใช้เวลายาวนานมากกว่าเดิม และ/หรืออาจจะเลิกล้มความตั้งใจไปก็เป็นได้
ถ้าอยากอ่านละเอียด ๆ กว่านี้เชิญที่ Link References ได้เลยครับละเอียดยิบเลย
ผิดพลาดประการใดขออภัยมา ณ ที่นี้ ยังไงก็ขอให้ทุกท่านมีความสุขกับการ Refactor Monolith to Microservices กันนะครับ