เช้านี้ได้อ่านโพสต์เรื่อง Packages as layers, not groups โดย Ben Johnson แล้วน่าสนใจ เลยมาสรุปเอาไว้หน่อย
สิ่งที่บทความพยายามสื่อคือ package ของ Go นั้นไม่ใช่แค่กลุ่มของฟังก์ชันที่ทำหน้าที่คล้ายกันอย่างเดียว package ของ Go นั้นถูกใช้เพื่อออกแบบ dependencies ของโค้ดภายใน package ด้วย ดังนั้น package ของ Go จะไม่ยอมให้มีการอ้างอิงกัน (depends ต่อกัน) ในลักษณะ cyclic เช่น A ใช้ B แล้ว B ก็ใช้ A แบบนี้ (ข้อจำกัดตรงนี้เนี่ยแหละที่ทำให้ Go compiler compile ได้เร็ว)
ดังนั้นเวลาออกแบบจะเห็นว่า package ถูกออกแบบเป็นชั้นๆ (layer) โดยหน้าที่ของชั้นล่างจะคอย support ฟังก์ชันการทำงานของชั้นถัดๆไป เช่น net/http เรียกใช้ net แล้ว net ไปเรียกใช้ io อีกที
จะเห็นว่าแต่ละชั้นก็จะ absract ความซับซ้อนให้กับชั้นด้านบน net ไม่ต้องเขียนการติดต่อ io เอง net/http ก็ไม่ต้องจัดการ socket เอง
ในการออกแบบ application ของเราเองก็เช่นกัน เขายกตัวอย่าง app ง่ายๆที่มี 2 layers และออกแบบโดยแยกเป็นแค่ 2 packages คือ http จัดการ http service endpoint ต่างๆ ส่วน sqlite จัดการข้อมูลที่ต้องเขียน/อ่านกับฐานข้อมูล sqlite
ทิศทางของ dependency ก็คือ http เรียกใช้ sqlite
แล้วเขาก็ refactor design ให้ดูว่าจริงๆยังแยกออกเป็น package สำหรับ business domain ได้นะ แล้ว sqlite package ก็จะเหลือแค่ส่วนจัดการ sqlite จริงๆ อะไรเกี่ยวกับ domain logic แยกออกมา ในตัวอย่างชื่อ package คือ wtf
สุดท้ายแยกอีกรอบคือทำให้ http ไม่ยึดโยงกับ sqlite โดยตรงเพื่อให้ง่ายต่อการเขียนเทสหรือเปลี่ยนไปใช้ db ตัวอื่น โดยการทำให้ http ขึ้นกับ interface ที่ออกแบบเพื่อติดต่อ db แล้วตัว interface นี้ก็กำหนดไว้ใน wtf package
จากบทความนี้ก็มีประโยชน์มากๆเลยในการออกแบบ package ให้เป็น layer โดยที่แต่ละ layer คอย abstract ฟังก์ชันการทำให้ให้ layer ที่เรียกใช้อีกทีนึง
Top comments (2)
Rust เหมือนกันไหมครับ ?
ยังไม่รู้เหมือนกันครับสำหรับ Rust