Micro service ไม่เท่ากับ Micro repositories (part1: ทำอะไรกันมา)
Tags: microservices, monorepo, repositorypattern
Published: 2021-11-21 10:15:04

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

อะ.. คงไม่ต้องพูดถึง Microservice หรืออธิบายอะไรกันแล้ว เพราะมีคนอธิบายกันไว้เยอะแล้ว และตัวมันเองก็พิสูจน์เยอะแล้วหล่ะ ซึ่งวันนี้เราจะไม่ได้มาพูดถึงตัว Microservice มัน แต่จะมาพูดถึงสิ่งที่หลายๆคน จะได้ติดมากันมันซึ่งก็คือ Micro repositories นั้นเอง

## Micro repositories
ก็คือเหตุการ์ณที่เมื่อเราต้องการจะขึ้น Microservice ใหม่อะไรก็ตาม แล้วเราก็เริ่มโดยการไปสร้าง repo ใหม่ setup โปรเจคใหม่ เรียกได้ว่าเป็น Fresh start ครั้งใหม่ หลายๆครั้งเราเลือก tech stack หรือการอัพเกรด Version ตรงนี้เลยด้วยซ้ำ เพราะ การอัพเกรด Repo เก่านั้นมีปัญหา หรือยุ่งยาก อะซึ่งมันก็เป็นนิสัยของ Developer ส่วนใหญ่อยู่ที่แล้วที่ต้องการจะแก้ปัญหาที่เกิดขึ้นใน Repo เก่าใน Repo ใหม่เพื่อไม่ให้เกิดปัญหาซ้ำเดิมขึ้นมา

ปัญหาที่เกิดขึ้นก็คือตรงนี้นั้นหล่ะครับ หลังจากที่เราขึ้น Repo ใหม่ไปเรื่อยๆ ตาม Service ที่มันขยาย หรือการทดลองอะไรใหม่ หรือบางครั้งแค่ความอยากใช้เล็กๆน้อยๆก็ตาม ซึ่งมันทำให้เกิดหลาย standard ขึ้นมาทันที และต้องการ ความรู้หรือ skill set ที่ต่างกันในการดูแล Repo นี้และมี มาตรฐานของ CI/CD ที่ไม่เท่ากัน เช่นบางครั้งเป็นเรื่องง่ายๆ Dev บางคนชอบ build code แล้วให้ออกใน folder /dist บางคนชอบ /build หรือการวาง file และชื่อที่ตั้งไม่เหมือนกัน ในระยะยาวเรากำลังสร้าง technical debt ก้อนใหญ่อยู่พอสมควรเหมือนกัน

> เรียกได้ว่า 'Moving too fast, broke too many thing'

อะซึ่งวิธีแก้ปัญหาของฝั่ง Microservice มันก็มีง่ายๆ ก็คือเมื่อมีการขึ้น Repo ใหม่ ใดๆก็ตาม และคุณมีการอัพเกรด Version หรือเปลี่ยน standard อะไรก็ตาม ต้องกลับมาทำแบบนี้กับทุก Service ที่มีเพื่อรักษา standard นั่นไว้

ซึ่ง… ในมุมมองของผมนั่น ถ้าเราไม่ได้ take advantage จาก Microservice ระดับเต็มที่แล้วหรือไม่ได้ใช้คอนเซ็ปของมันระดับสุดขั้ว เช่น
- utility ต่างๆ แบบ format string หรือ การแปลงวันที่ ต่างๆ ไม่ได้ถูกเขียนขึ้นมาเป็น library หรือ git submodule
- Api ที่เชื่อมต่อกับระบบอื่นๆ ซึ่งเป็น Share code ไม่ได้ถูกนำไปแยกเป็น Service แต่ถูกเขียนขึ้นมาให้แต่ละ Repo โดยเฉพาะ
- IaC ไม่แข็งแรง ไม่เป็น Template การขึ้น Service ใหม่ใน production มีความยุ่งยากและกินเวลา
- และสุดท้ายหนึ่งในสิ่งที่ยากที่สุด คือการแยก database ของแต่ละ service ออกจากกันและการ maintain แต่ละตัว

และจากประสบการณ์ผมแล้ว size ของทีมมีผลอย่างมากเลยทีเดียวกับเรื่องพวกนี้ หากคุณเป็น ทีม size เล็กราวๆ 4~8 คนและเป็น Full stack ไม่ได้มีทีม Ops มาช่วยดูแล หรือการ maintain อะไรพวกนี้ และบ่อยครั้ง โดนกดดันโดย timeline ให้มีการลัดและข้ามขั้นตอนบางอย่างไปเพื่อ delivery งาน ในระยะยาวแล้ว Microservice จะสร้างปัญหาให้เราอย่างมากเลยทีเดียว (จะขอเวลากลับไปอัพเกรด Version เก่าทำไมในเมื่อมันยังทำงานอยู่ได้)

> *Case study สั้นๆ*
> ทีมผมได้มีการเชื่อมต่อ Service กับ Facebook API และเมื่อ Facebook ทำการ ยกเลิก api version เก่าๆ การมานั่งไล่อัพเดทแต่ละ Service ที่บางครั้งเขียนโดนคนละคน คนละ Version หรือบางทีคนละภาษา ไม่ใช่เรื่องสนุกเท่าไหร

## Monorepo but build into microservice

![Repo pattern](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/spprx6wj24pw6xxrnv5t.png)
 

เป็นหนึ่งใน Solution ที่ถูกเสนอขึ้นมาแก้ปัญหาเหล่านี้ (ซึ่งตัวมันเองก็มีปัญหาของมันเช่นเดียวกัน) ก็คือการที่เรามี Repository เก็บ code ที่เดียว (ในกรณีที่ tech stack เป็นสิ่งเดียวกัน) ใช้ standard เดียวกันใช้ CI/CD ร่วมกัน ของบางอย่างแทนที่จะต้องเขียนเป็น lib หรือตั้ง service สามารถจบได้โดยการเป็น share code

หลักการก็ง่ายๆ ก็คือเรามีการจัดสรร Folder อย่างดี แบ่งระหว่าง code ที่เป็นของใช้ร่วมกันหลายๆ service และ code แยกแต่ละของ service แล้วเมื่อสั่ง build ตัว image ในการ deploy เราก็จะ copy เฉพาะ code ส่วนที่จำเป็นและ install package เท่าที่จำเป็น ซึ่งในที่นี้ ผมแยกโดยการใช้ release tag ของแต่ละ service

ตัวอย่างง่ายๆ เช่นเราไม่ได้แยก database ของแต่ละ Service ออกจากกัน แต่เราทำ database shard แทน (เก็บข้อมูลประเภทเดียวกันแต่แยก table เพราะต้องการให้ table มีขนาดเล็ก อาจจะแยกตาม ลูกค้า ประเทศ … etc) การเขียน ORM ขึ้นมาก็จะสามารถนำไปแชร์กันได้ระหว่าง Service ซึ่งสามารถดูแลได้ง่ายๆ

หลักๆ คือเราไม่ได้ใช้พลังของ Microservice เต็มที่ แต่ยังต้องการบางอย่างจากมันอยู่เช่น deploy แยกแต่ละ service เพื่อให้เกิด downtime และผลกระทบน้อย หรือการ ต้องการจะทำ auto scaling ที่ level service ไม่สนที่ level database
ซึ่งแน่นอนไอการจะทำสิ่งนี้ก็ต้องมี tools ที่ใช้ช่วยในการจัดการเยอะพอสมควร ตัวอย่าง git submodule, [Lerna](https://lerna.js.org) หรือ [Rushjs](https://rushjs.io) และมีความหลากหลายวิธีมากๆ ในการวางโครงสร้าง ซึ่งผมจะกลับมาพูดถึงในบทถัดไป ถ้ายังมีแรงเขียนนะครับ

## สรุปแล้วมันจะมีปัญหาอะไรบ้าง ไอตัวนี้

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

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