- 目錄結(jié)構(gòu)
- Go 目錄
- 服務(wù)端應(yīng)用程序目錄
- Web 應(yīng)用程序目錄
- 通用應(yīng)用程序目錄
- 其他目錄
- 不應(yīng)該出現(xiàn)的目錄
- 其他文件
- 小結(jié)
- 延伸閱讀
- 參考
目錄結(jié)構(gòu)
項(xiàng)目的目錄結(jié)構(gòu)通常也是門面况凉,內(nèi)行人通過目錄結(jié)構(gòu)基本就能看出開發(fā)者是否有經(jīng)驗(yàn)添吗。
Go 官網(wǎng)并沒有給出一個目錄結(jié)構(gòu)的標(biāo)準(zhǔn)模板酪呻,但是 golang-standards 倒是給出了一個,目錄結(jié)構(gòu)如下:
├── api
├── assets
├── build
│ ├── ci
│ └── package
├── cmd
│ └── _your_app_
├── configs
├── deployments
├── docs
├── examples
├── githooks
├── init
├── internal
│ ├── app
│ │ └── _your_app_
│ └── pkg
│ └── _your_private_lib_
├── pkg
│ └── _your_public_lib_
├── scripts
├── test
├── third_party
├── tools
├── vendor
├── web
│ ├── app
│ ├── static
│ └── template
├── website
├── .gitignore
├── LICENSE.md
├── Makefile
├── README.md
└── go.mod
Go 目錄
cmd
當(dāng)前項(xiàng)目的可執(zhí)行文件扔亥。cmd
目錄下的每一個子目錄名稱都應(yīng)該匹配可執(zhí)行文件。比如果我們的項(xiàng)目是一個 grpc
服務(wù)膘融,在 /cmd/myapp/main.go 中就包含了啟動服務(wù)進(jìn)程的代碼票髓,編譯后生成的可執(zhí)行文件就是 myapp。
不要在 /cmd
目錄中放置太多的代碼掷空,我們應(yīng)該將公有代碼放置到 /pkg
中,將私有代碼放置到 /internal
中并在 /cmd
中引入這些包囤锉,保證 main 函數(shù)中的代碼盡可能簡單和少坦弟。
例子:
- https://github.com/heptio/ark/tree/master/cmd
- https://github.com/moby/moby/tree/master/cmd
- https://github.com/prometheus/prometheus/tree/master/cmd
- https://github.com/influxdata/influxdb/tree/master/cmd
- https://github.com/kubernetes/kubernetes/tree/master/cmd
- https://github.com/satellity/satellity/tree/master/cmd
- https://github.com/dapr/dapr/tree/master/cmd
internal
私有的應(yīng)用程序代碼庫。這些是不希望被其他人導(dǎo)入的代碼官地。請注意:這種模式是 Go 編譯器強(qiáng)制執(zhí)行的酿傍。詳細(xì)內(nèi)容情況 Go 1.4 的 release notes。并且驱入,在項(xiàng)目的目錄樹中的任意位置都可以有 internal 目錄赤炒,而不僅僅是在頂級目錄中。
可以在內(nèi)部代碼包中添加一些額外的結(jié)構(gòu)亏较,來分隔共享和非共享的內(nèi)部代碼莺褒。這不是必選項(xiàng)(尤其是在小項(xiàng)目中),但是有一個直觀的包用途是很棒的雪情。比如:應(yīng)用程序代碼放在 /internal/app
目錄(如遵岩,internal/app/myapp
),而應(yīng)用程序的共享代碼放在 /internal/pkg
目錄(如,internal/pkg/myprivlib
)中尘执。
pkg
外部應(yīng)用程序可以使用的庫代碼(如舍哄,/pkg/mypubliclib
)。其他項(xiàng)目將會導(dǎo)入這些庫來保證項(xiàng)目可以正常運(yùn)行誊锭,所以在將代碼放在這里前表悬,一定要三四而行。請注意丧靡,internal 目錄是一個更好的選擇來確保項(xiàng)目私有代碼不會被其他人導(dǎo)入蟆沫,因?yàn)檫@是 Go 強(qiáng)制執(zhí)行的。使用 /pkg
目錄來明確表示代碼可以被其他人安全的導(dǎo)入仍然是一個好方式窘行。Travis Jeffery 撰寫的關(guān)于 I’ll take pkg over internal 文章很好地概述了 pkg
和 inernal
目錄以及何時使用它們饥追。
當(dāng)根目錄包含大量非 Go 組件和目錄時,這也是一種將 Go 代碼分組到一個位置的方法罐盔,從而使運(yùn)行各種 Go 工具更加容易(在如下的文章中都有提到:2018 年 GopherCon Best Practices for Industrial Programming但绕,GopherCon 2018: Kat Zien - How Do You Structure Your Go Apps,Golab 2018 Massimiliano Pippi - Project layout patterns in Go惶看。
/pkg 在許多開源項(xiàng)目中都使用了捏顺,但未被普遍接受,并且 Go 社區(qū)中的某些人不推薦這樣做纬黎。
如果項(xiàng)目確實(shí)很小并且嵌套的層次并不會帶來多少價值(除非你就是想用它)幅骄,那么就不要使用它。但當(dāng)項(xiàng)目變得很大本今,并且根目錄中包含的內(nèi)容相當(dāng)繁雜(尤其是有很多非 Go 的組件)時拆座,可以考慮使用 /pkg
。
vendor
應(yīng)用程序的依賴關(guān)系(通過手動或者使用喜歡的依賴管理工具冠息,如新增的內(nèi)置 Go Modules 特性)挪凑。執(zhí)行 go mod vendor
命令將會在項(xiàng)目中創(chuàng)建 /vendor
目錄,注意逛艰,如果使用的不是 Go 1.14 版本躏碳,在執(zhí)行 go build
進(jìn)行編譯時,需要添加 -mod=vendor
命令行選項(xiàng)散怖,因?yàn)樗皇悄J(rèn)選項(xiàng)菇绵。
構(gòu)建庫文件時,不要提交應(yīng)用程序依賴項(xiàng)镇眷。
請注意咬最,從 1.13 開始,Go 也啟動了模塊代理特性(使用 https://proxy.golang.org 作為默認(rèn)的模塊代理服務(wù)器)欠动。點(diǎn)擊這里閱讀有關(guān)它的更多信息丹诀,來了解它是否符合所需要求和約束。如果 Go Module 滿足需要,那么就不需要 vendor 目錄铆遭。
注:在 Go 語言中組織代碼的方式還有一種叫”平鋪“的硝桩,也就是在根目錄下放項(xiàng)目的代碼。這種方式在很多框架或者庫中非常常見枚荣,如果想要引入一個使用 pkg 目錄結(jié)構(gòu)的框架時碗脊,我們往往需要使用
github.com/golang/project/pkg/somepkg
,當(dāng)代碼都平鋪在項(xiàng)目的根目錄時只需要使用github.com/golang/project
橄妆,很明顯地減少了引用依賴包語句的長度衙伶。所以,對于一個 Go 語言的框架或者庫害碾,將代碼平鋪在根目錄下也很正常矢劲,但是在一個 Go 語言的服務(wù)中使用這種代碼組織方法可能就沒有那么合適了。
服務(wù)端應(yīng)用程序目錄
api
項(xiàng)目對外提供和依賴的 API 文件慌随。比如:OpenAPI/Swagger specs, JSON schema 文件, protocol 定義文件等芬沉。
比如,Kubernetes 項(xiàng)目的 api 目錄結(jié)構(gòu)如下:
api
├── api-rules
└── xxx.plist
├── openapi-spec
└── swagger.json
因此阁猜,在 go 中用的比較多的 gRPC proto 文件丸逸,也比較適合放在 api 目錄下。
api
└── protobuf-spec
└── test
├── test.pb.go
└── test.proto
Web 應(yīng)用程序目錄
web
Web 應(yīng)用程序特定的組件:靜態(tài) Web 資源剃袍,服務(wù)器端模板和單頁應(yīng)用(Single-Page App黄刚,SPA)
通用應(yīng)用程序目錄
build
打包和持續(xù)集成所需的文件。
- build/ci:存放持續(xù)集成的配置和腳本民效,如果持續(xù)集成平臺對配置文件有路徑要求憔维,則可將其 link 到指定位置。
- build/package:存放 AMI畏邢、Docker埋同、系統(tǒng)包(deb、rpm棵红、pkg)的配置和腳本等。
例子:
configs
配置文件模板或默認(rèn)配置咧栗。
deployments
IaaS逆甜,PaaS,系統(tǒng)和容器編排部署配置和模板(docker-compose致板,kubernetes/helm交煞,mesos,terraform斟或,bosh)素征。請注意,在某些存儲庫中(尤其是使用 kubernetes 部署的應(yīng)用程序),該目錄的名字是 /deploy御毅。
init
系統(tǒng)初始化(systemd根欧、upstart、sysv)和進(jìn)程管理(runit端蛆、supervisord)配置凤粗。
scripts
用于執(zhí)行各種構(gòu)建,安裝今豆,分析等操作的腳本嫌拣。
這些腳本使根級別的 Makefile 變得更小更簡單,例如:https://github.com/hashicorp/terraform/blob/master/Makefile呆躲。
test
外部測試應(yīng)用程序和測試數(shù)據(jù)异逐。隨時根據(jù)需要構(gòu)建 /test
目錄。對于較大的項(xiàng)目插掂,有一個數(shù)據(jù)子目錄更好一些灰瞻。例如,如果需要 Go 忽略目錄中的內(nèi)容燥筷,則可以使用 /test/data
或 /test/testdata
這樣的目錄名字箩祥。請注意,Go 還將忽略以“.”或“_”開頭的目錄或文件肆氓,因此可以更具靈活性的來命名測試數(shù)據(jù)目錄袍祖。
其他目錄
assets
項(xiàng)目中使用的其他資源(圖像、logo 等)谢揪。
docs
設(shè)計(jì)和用戶文檔(除了 godoc 生成的文檔)蕉陋。
examples
應(yīng)用程序或公共庫的示例程序。
githooks
Git 鉤子拨扶。
third_party
外部輔助工具凳鬓,fork 的代碼和其他第三方工具(例如:Swagger UI)。
tools
此項(xiàng)目的支持工具患民。請注意缩举,這些工具可以從 /pkg
和 /internal
目錄導(dǎo)入代碼。
例子:
- https://github.com/istio/istio/tree/master/tools
- https://github.com/openshift/origin/tree/master/tools
- https://github.com/dapr/dapr/tree/master/tools
website
如果不使用 Github pages匹颤,則在這里放置項(xiàng)目的網(wǎng)站數(shù)據(jù)仅孩。
例子:
- https://github.com/hashicorp/vault/tree/master/website
- https://github.com/perkeep/perkeep/tree/master/website
不應(yīng)該出現(xiàn)的目錄
src
有一些 Go 項(xiàng)目確實(shí)包含 src
文件夾,但通常只有在開發(fā)者是從 Java(這是 Java 中一個通用的模式)轉(zhuǎn)過來的情況下才會有印蓖。如果可以的話請不要使用這種 Java 模式辽慕。你肯定不希望你的 Go 代碼和項(xiàng)目看起來向 Java。
不要將項(xiàng)目級別的 /src
目錄與 Go 用于其工作空間的 /src
目錄混淆赦肃,就像 How to Write Go Code中描述的那樣溅蛉。$GOPATH
環(huán)境變量指向當(dāng)前的工作空間(默認(rèn)情況下指向非 Windows 系統(tǒng)中的$HOME/go)公浪。此工作空間包括頂級 /pkg
,/bin
和 /src
目錄船侧。實(shí)際的項(xiàng)目最終變成 /src
下的子目錄欠气,因此,如果項(xiàng)目中有 /src
目錄勺爱,則項(xiàng)目路徑將會變成:/some/path/to/workspace/src/your_project/src/your_code.go
晃琳。請注意,使用 Go 1.11琐鲁,可以將項(xiàng)目放在 GOPATH
之外卫旱,但這并不意味著使用此布局模式是個好主意。
其他文件
Makefile
在任何一個項(xiàng)目中都會存在一些需要運(yùn)行的腳本围段,這些腳本文件應(yīng)該被放到 /scripts
目錄中并由 Makefile 觸發(fā)顾翼。
小結(jié)
每個公司、組織內(nèi)部都有自己的組織方式奈泪,但每個項(xiàng)目都應(yīng)該有一定的規(guī)范适贸。雖然這種規(guī)范的約定沒有那么強(qiáng)制,但是只要達(dá)成了一致之后涝桅,對于團(tuán)隊(duì)中組員快速理解和入門項(xiàng)目都是很有幫助的拜姿。有時候一些規(guī)范,就是團(tuán)隊(duì)的共同語言冯遂,定好了規(guī)范蕊肥,減少了不必要的重復(fù)溝通,有利于提高整體的效率蛤肌。
項(xiàng)目目錄也一樣壁却,本篇文章講的是參考 golang-standards 提供的規(guī)范。但是裸准,最重要的還是要與自己的團(tuán)隊(duì)商量展东,討論并整理出適合自己的一套項(xiàng)目目錄規(guī)范。
一致的項(xiàng)目目錄規(guī)范炒俱,有助于組員快速理解其他人的代碼盐肃,不容易造成團(tuán)隊(duì)的”單點(diǎn)故障“;團(tuán)隊(duì)團(tuán)結(jié)一致权悟,共同維護(hù)和升級項(xiàng)目目錄結(jié)構(gòu)砸王,可不斷沉淀,不斷提高效率僵芹,減少犯錯。
延伸閱讀
- 在 Golang 上使用整潔架構(gòu)(Clean Architecture)
- 在 Golang 上使用整潔架構(gòu)(Clean Architecture)-2
- 在 Golang 上使用整潔架構(gòu)(Clean Architecture)-3
- Effective Go 中文
- Code Review 規(guī)范