Refactoring Monolith to Microservices by using Strangler Pattern

Sakul Montha
4 min readJul 25, 2022

ผมค่อนข้างมั่นใจว่าหลายคนที่เข้ามาอ่าน หน้าจะรู้จัก หรือเคยได้มีโอกาสพัฒนา Project ที่มีสถาปัตยกรรม ทั้งแบบ Monolith และแบบ Microservices กันมาอยู่แล้ว ขึ้นอยู่ว่าจะมาก หรือจะน้อยอย่างไร

บทความนี้จะไม่ขอพูดถึงความแตกต่างของมัน แต่เราจะมาพูดถึงวิธีการ Refactor จาก Monolith ไปสู่ Microservices ด้วยวิธี Strangler Pattern กัน

REFACTORING MONOLITH TO MICROSERVICES BY USING 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 ออกจากกัน

AS IS -> TO BE

แตก 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 ดังภาพด้านล่างนี้

AS IS

นี่เป็นเพียงภาพตัวอย่างคร่าว ๆ นะครับ ถ้าสงสัย เชื่อว่าสงสัยแหละ อย่าสงสัยเยอะ มันหลวม ๆ เท่านั้น

Split code

เริ่มต้นด้วยทำการแบ่งแยก Code และ Convert ตัว Trade Management ออกเป็น Module ให้มันมี Dependency กันน้อย ๆ หลวม ๆ ภายใน Monolith ของเรา

อาจจะใช้ท่า Interface ก็ได้ในการคุยกันระหว่าง Order กับ Trade เพื่อให้เกิด Loosely coupled โดยที่ Trade เป็น Services ใหม่ที่อยู่ใน Monolith

ที่สำคัญพอแยกแล้วมันทำให้เราเขียน Test ได้ด้วย (ในกรณีที่ของเดิมมันไม่มี Test) ถ้าของเดิมมี Test อยู่แล้วยิ่งดีครับ ได้ใช้ TDD กันสนุกมือ

Split Code

Split Database

ทำการ Split database และทำการกำหนด Schema ขึ้นมาแยกต่างหาก สำหรับ Trade management ของเรา

ระหว่างการแยกต้องคำนึงถึงพวกเรื่อง Trigger ระหว่าง 2 DB ด้วยระหว่างที่ยัง Implement และที่สำคัญอย่าลืมเรื่อง Migration ด้วยนะครับ

Split DB

Define Standalone Service

สร้าง Standalone service ขึ้นมา ซึ่งนั่นก็คือ Trade Management Service นั่นเองจากนั้นลองเอามันขึ้นไปใช้ดูก่อนว่ามันสามารถใช้งานได้อย่างถูกต้องมั้ย แบบคร่าว ๆ ถ้าดูจากรูปจะเห็นว่า มันแค่เกิด Services ใหม่ขึ้นมา ยังไม่ได้ใช้จริงนะ คือมีเอาไว้ใช้เทสก่อน ยิงเข้ามาได้ แต่อาจจะไม่ได้เปิดให้ ลูกค้าจริง ๆ ใช้งาน

Define Standalone Service

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ถ้าอยากได้ข้อมูลเพิ่มเติมเชิญไปอ่านกันได้ที่นี่ครับ

Use Standalone Service

Remove Trade function from Gique System

ขั้นตอนสุดท้ายแล้ว! ทำการ ลบ Function ใด ๆ ที่เกี่ยวข้องกับ Trade ที่อยู่ใน Monolith ออกจากระบบให้หมด ไม่ว่าจะเป็น Controller, Services, หรือ Repositories ไม่ต้องกล้วพัง ถ้าพัง มันพังไปนานแล้ว จากข้อข้างบน ๆ หรือไม่ก็ลบเกิน…

Removing the obsolete code

จะเห็นว่า 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 กันนะครับ

--

--

Sakul Montha

Chief Product Officer, a man who’s falling in love with the galaxy.