Go kit ... first time
Tags: go, microservices
Published: 2020-12-27 08:49:19

Go kit เป็น Library ตัวหนึ่ง ที่นำเสนอเรื่องรูปแบบ Structure เอาไว้จัดโครงสร้างของ code ให้เหมาะกับการทำ Micro service บางทีก็ถูกย้อนแย้งว่ามันเป็น Elegant Monolith ซะมากกว่า ซึ่งผมก็เห็นด้วยนิดๆนะ ติดว่าถ้าไม่จำเป็นต้องแยก service ออกมาจริงๆ implement แบบรวมๆไปเลยเป็น Monolith สบายกว่าเยอะจ้า

Go kit เสนอแนวคิด โดยให้แบ่ง structure ออกเป็น 3 domain (จากด้านในไปด้านนอก)
  - **Service** ดูแล business logic ใน layer เราสามารถแบ่งออกเป็น layer ย่อยๆ แบบใน clean architecture ได้เช่น data model, repository, logic layer
  - **Endpoint** เป็นตัวคั่น business logic ให้แยกออกจากส่วนจัดการการรับส่ง request (transport layer เช่น HTTP, gRPC, ...) ในชั้นนี้จะรับ [Request] object จากชั้น Transport แล้วแกะ parameters `(p1, p2, p3, ... pn)` ที่ต้องการออกจาก [Request] object และไป call function ที่ผูกไว้กับ ชั้น Service `serviceFunc1(p1, p2, p3, ... pn)` และเอา return Values ที่ได้จากการ call function มารวมกันเป็น [Response] object ส่งกลับออกไปที่ชั้น Transport เพื่อให้ส่งกลับไปให้ client 
  - **Transport** เป็น layer ที่คอยจัดการวิธีรับส่งข้อมูล ด้วย protocol ต่างๆเช่น HTTP, gRPC, ... ในชั้นนี้จะทำหน้าที่แกะเอา parameter ออกจาก Request (กรณีของ HTTP server ก็จะอ่าน parameters จาก Header, Query string, Form หรือ Request body) แล้วเอามาประกอบเป็น [Request] object เพื่อส่งต่อไปที่ Endpoint ที่เชื่อมไว้ และเมื่อได้รับ [Response] object กลับมาจาก Endpoint ก็เอาไปแปลงเป็น Response ในรูปแบบของ Protocol ต่างๆ และส่งกลับไปให้ client (หรืออีก service ที่เป็นคนส่ง request มา) 

ตัวอย่าง Flow ของ data ที่วิ่งผ่านระหว่าง layer ต่างๆ โดยใช้ HTTP server เป็นชั้น Transport

1. ในชั้น Transport HTTP server รับ HTTP request มา
2. แกะ parameter แต่ละตัวออกมาจาก Request body, Header, Form values, หรือ Query string และประกอบกันเป็น [Request] object 
3. ส่ง [Request] object ต่อไปที่ Endpoint ที่ผูกไว้กับ api path นั้นๆ
4. Endpoint รับเอา [Request] object มา แล้วแกะเอา parameter ออกมาเป็น parameters ต่างๆ `p1, p1, ..., pn`
5. call function ในชั้น Service `r1, r2, r3, ... rn = serviceFunction(p1, p2 , p3,... pn)` และเอา returned values `r1, r2, r3, ... rn` มาประกอบกันเป็น [Response] object ส่งกลับไปที่ Endpoint
6. Endpoint ส่ง [Response] object ไปที่ชั้น Transport
7. Transport layer เอา [Response] object มาแปลงเป็น HTTP Response และส่งกลับไปให้ client

ความเห็นส่วนตัวหลังจากลอง migrate project เดิม
- :smile: แยก Business logic ออกมาได้ชัดเจนขึ้น สามารถแก้ไขแต่ละ layer โดยไม่กระทบกัน
- :smile: ถ้าวาง function ที่รองรับ Business logic ไว้แล้วใน Service layer (และมี function signature ที่นิ่งพอแล้ว) สามารถกระจายงานให้ programmer แยกย้ายกันไปจัดการใน layer ต่างๆได้เลย
- :satisfied: กำลังสนุกกับการใช้ Middleware มันสามารถทำ logging middleware เพื่อ log event ในแต่ละ layer ได้เลย หรือทำ rate limit, load balance และแต่ละ Middleware สามารถถอดเข้าออกได้โดยที่ไม่กระทบมากนัก
- :sweat: Refractor เหนื่อยหน่อย ถ้ามีการเปลี่ยน function signature จาก Service layer แล้วล่ะก็ กระทบชิ่งยันชั้น Transport layer เลย
- :sweat: หลังจากลอง implement ไป 3 service พบว่าตัวเองต้องเขียน code เยอะมาๆ และใช้เวลานานเลยทีเดียว อย่างตอนทำ service Leaderboard ที่มีประมาณ 6 endpoint ก็ใช้เวลาไปประมาณ 8-10 ชั่วโมง
- :roll_eyes: ถึงแม้จะทำ code snippet เก็บไว้ใช้ มันก็ช่วยลดเวลา และ bug ได้อยู่บ้าง แต่ก็ยังต้อง code เยอะอยู่ดี คิดว่าถ้าใช้จนคล่อง และแม่นขึ้นแล้วจะไปลองใช้ tool พวก code generator ดูบ้าง 

จบเพียงเท่านี้กับ Blog สั้นๆเรื่อง ครั้งแรกกับ Go kit แล้วถ้าผมเข้าใจเรื่อง Middleware มากขึ้นแล้วจะมาเขียน share ให้อีกทีจ้า