วันนี้จะมาเล่าเรื่อง Project Layout ครับ เรียกได้ว่าเป็นหนึ่งในสิ่งสำคัญที่จะทำให้เราพัฒนา application ด้วยภาษา Golang ได้ตรงตามที่ผู้พัฒนาต้องการให้เราทำ ( หรือ Idiomatic ที่แปลว่า ลักษณะจำเพาะ ) เพื่อให้โปรเจคเรา ใกล้เคียงความเป็น Gopher project มากที่สุดกันครับ
✨ TL;DR อีกแล้ว
Golang ไม่มี layout standard เป็นตัวเป็นตนจาก Go Core Dev team ที่มีอยู่ตอนนี้ก็คือ ตั้งแต่ Go 1.4
เป็นต้นไป folder internal
จะไม่สามารถถูกเรียกใช้จากนอก package หลักได้
นอกนั้นก็ตามแล้วแต่ผู้ดูแลโปรเจคจะเป็นคนกำหนด convention ของ layout ตัวเอง
ส่วนทางด้าน community และ open source ก็มีตัวอย่างที่ใช้กันบ่อยๆ ฮิตๆก็ดูได้จาก Repository นี้ (และ open source ของ Golang อื่นๆ)
https://github.com/golang-standards/project-layout
หลักๆคือ
cmd
ใช้สำหรับเก็บ package ที่เป็น entry point ของ application (ส่วนใหญ่ก็คือ package main) โดยจะไม่นำ code มาเขียนตรงนี้เยอะ โดย package ในcmd
จะไปเรียกใช้ code ที่อยู่ในส่วนของinteral
และpkg
internal
เก็บ package ที่เป็น implementation หลักของ application (หรือ library) ซึ่งไม่ต้องการให้มีการ import จากนอก packagepkg
ใช้เก็บ package ที่เหมือนกันinternal
แต่สามารถ import ไปใช้งานได้นอก application (หรือ library) เหมาะสำหรับใช้ reuse package ไปที่ project อื่นๆ
และนอกจากนี้ เรายังสามารถศึกษาได้จาก Open Source Project อื่นๆที่พัฒนาด้วย Golang (ดูต่อที่ Reference)
แต่ยังไงก็ตาม การทำงานที่มีประสิทธิภาพที่สุด ก็คือการทำงานบน convention ที่เราสามารถสร้าง productivity ได้มากที่สุด เพราะฉะนั้น ก็ไม่จำเป็นจะต้องทำตามสิ่งเหล่านี้ทั้งหมดครับ เราอาจจะใช้ layout ของ NodeJS หรือ Java ก็ไม่เป็นอะไร ถ้าเราสามารถ deliver งานได้ตามที่ business ต้องการ
เริ่มเนื้อหาแบบยาว...
Project Layout คืออะไร?
ในการพัฒนาแอพพลิเคชั่นแต่ละภาษา ก็จะมี Project Layout แตกต่างกันออกไปครับ เพื่อการ organize code ที่ดี แต่ละภาษาก็มี convention แตกต่างกันออกไปตาม design pattern ของภาษา
เช่นเมื่อ New Project ใน Java
, C#
, Swift
หรือ NodeJS
รวมไปถึงระดับ Framework เช่น Laravel หรือ Angular เราก็จะได้ project layout แตกต่างกันออกไป แต่ก็เพื่อให้สามารถจัดการ Code ง่ายที่สุด ตามที่ผู้พัฒนาภาษาคิดมาซึ่งส่วนใหญ่ หนีไม่พ้น ทุกแบบก็จะประกอบไปด้วย
- Dependencies and Information
- Implementation
- Testing
- Build
- Distributed
เช่น Swift
เราก็จะมี Podfile
, xcodeproj
หรือ NodeJS
เราจะมี package.json
, src
folder
Dependencies and Project Information
สำหรับ Golang แล้ว ก่อนที่จะมี go mod
เราบันทึก dependencies ไว้ภายในตัว code เอง (ใน import) แต่หลังจากนั้นก็มี tools เพื่อควบคุม version เช่น glider
หรือ godeps
ปัจจุบัน Golang มี dependency management เองแล้วชื่อ go mod
ซึ่งประกอบด้วย go.mod
และ go.sum
ภายในบ่งบอกถึง dependencies ต่างๆที่เราจะใช้งาน
ส่วน Project Information ต้องบอกก่อนว่า Golang เราทำ document ด้วยการ comment เป็นหลัก (เรื่องการทำ godoc
จะเอาไว้เขียนบทความหน้า) เพราะฉะนั้น Information ของ package ต่างๆก็เลยถูกเขียนไว้ใน comment ทั้งหมด
Implementation
ส่วนที่เราเก็บ code ที่เราเขียนไว้จะประกอบด้วย 4 packages หลักๆ (จริงๆมีมากกว่านั้น แต่ผู้เขียนเลือกอันที่ใช้ และเจอบ่อยมาเพื่อความเรียบง่าย ลองดูใน Go Standard Layout เพิ่มเติมครับ)
1. cmd Folder
Folder หลักที่ใช้เก็บ Entry point package ต่างๆของ application โดยส่วนมากจะเป็น Package main โดย package ภายใต้ folder นี้ไม่จำเป็นจะต้องมี implementation ที่ซับซ้อนมากมาย ทำแค่เพียงเป็น entry point เรียก implementation อื่นๆจาก package ใน folder internal
, web
และ pkg
ตัวอย่างจาก Ethereum Golang
ตัวอย่างจาก Go Pkgsite
2. internal Folder
Folder นี้น่าจะเป็น folder ที่มี source code เก็บอยู่มากที่สุด (ถ้าเป็น application) เพราะประกอบด้วย implementation หลักของ Project ที่ไม่สามารถ import ผ่าน package อื่นๆนอกจาก Project นี้ได้ (หรือภายใต้ go module
เดียวกัน)
ตัวอย่างจาก Go Pkgsite ซึ่ง package แน่นมาก
ถึงใน standard project layout จะบอกว่าเป็น folder ที่อยู่ด้านนอกสุด (workspace root) แต่ก็ไม่จำเป็น เพราะ Golang จะ recognize folder internal ให้ตลอด เพราะฉะนั้นไม่จำเป็นที่จะต้องอยู่ที่ workspace root ก็ได้
เช่นตัวอย่างจาก Golang source code เองก็มีการวาง folder internal ไว้ภายใน src
folder ( ซึ่ง src
folder ไม่ได้มีใน Standard Project Layout )
3. pkg Folder ( หรืออยู่ที่ root ไปเลย )
คล้ายๆกันกับ internal
(อ่าว พูดเหมือนใน TL;DR เลย) แต่ในนี้จะประกอบด้วย code ที่สามารถนำไป reuse ใช้งานภายนอก package หลักได้
หรือบาง repository ก็ไม่ใช้เลย โดยนำเอา package ต่างๆมาไว้ที่ workspace root (ที่เดียวกับ go.mod
ไปเลย) โดยมีการเถียงกันที่ issue นี้ 555555
https://github.com/golang-standards/project-layout/issues/10
ถ้าดูตัวอย่าง ส่วนใหญ่จะเป็น Library หรือ Opensource tools ต่างๆ เช่น
ตัวอย่างจาก Moby (container engine ของ docker)
ตัวอย่างจาก Kubernetes
ตัวอย่างจาก Gorilla Mux ที่ไม่ใส่ folder อะไรเลย เอาไว้มันโต้งๆนี่แหล่ะ
4. web Folder
Folder นี้จะประกอบด้วย source code ของ frontend ซะส่วนใหญ่ (ถ้าเป็น web base application) โดยเราจะเอา html template, static asset ต่างๆมาเก็บไว้ในนี้ หรือถ้าเป็น Javascript framework อย่าง Angular ก็นำมาเก็บไว้ในนี้ได้ (ในกรณี Monorepository)
ตัวอย่างจาก traefik (http engine ที่เขียนด้วย golang (คล้ายๆ nginx)
go pkgsite นั้นเอาไว้ใน content
folder แทน go pkgsit repository
จบแล้วสำหรับ part แรก เดี๋ยวส่วนอื่นๆที่เหลือ จะเขียนต่อใน part ต่อไปครับ
Happy Christmas and coding!
🎄 🤖
Reference
- https://golang.org/doc/code.html
- https://github.com/golang/go
- https://github.com/golang-standards/project-layout
- https://github.com/gorilla/mux
- https://github.com/kubernetes/kubernetes
- https://github.com/golang/pkgsite/commit/4ad1f04766f983b16d42c752c6e12f011f155207
- https://github.com/moby/moby
- https://github.com/traefik/traefik
Top comments (0)