Go 学习笔记(84)— Go 项目目录结构
1. 目錄規范
一個好的目錄結構至少要滿足以下幾個要求。
- 命名清晰:目錄命名要清晰、簡潔,不要太長,也不要太短,目錄名要能清晰地表達出該目錄實現的功能,并且目錄名最好用單數。一方面是因為單數足以說明這個目錄的功能,另一方面可以統一規范,避免單復混用的情況。
- 功能明確:一個目錄所要實現的功能應該是明確的、并且在整個項目目錄中具有很高的辨識度。也就是說,當需要新增一個功能時,我們能夠非常清楚地知道把這個功能放在哪個目錄下。
- 全面性:目錄結構應該盡可能全面地包含研發過程中需要的功能,例如文檔、腳本、源碼管理、API 實現、工具、第三方包、測試、編譯產物等。
- 可預測性:項目規模一定是從小到大的,所以一個好的目錄結構應該能夠在項目變大時,仍然保持之前的目錄結構。
- 可擴展性:每個目錄下存放了同類的功能,在項目變大時,這些目錄應該可以存放更多同類功能。
根據功能,我們可以將目錄結構分為結構化目錄結構和平鋪式目錄結構兩種。
- 結構化目錄結構主要用在
Go應用中,相對來說比較復雜; - 而平鋪式目錄結構主要用在
Go包中,相對來說比較簡單;
2. 平鋪式目錄結構
一個 Go 項目可以是一個應用,也可以是一個代碼框架 / 庫,當項目是代碼框架 / 庫時,比較適合采用平鋪式目錄結構。
平鋪方式就是在項目的根目錄下存放項目的代碼,整個目錄結構看起來更像是一層的,例如 log 包 github.com/golang/glog 就是平鋪式的,目錄如下:
$ ls glog/
glog_file.go glog.go glog_test.go LICENSE README
3. 結構化目錄結構
當前 Go 社區比較推薦的結構化目錄結構是 https://github.com/golang-standards/project-layout。雖然它并不是官方和社區的規范,但因為組織方式比較合理,被很多 Go 開發人員接受。
├── api
├── assets
├── build
├── cmd
├── configs
├── deployments
├── docs
├── examples
├── githooks
├── go.mod
├── init
├── internal
├── LICENSE.md
├── Makefile
├── pkg
├── README_zh-CN.md
├── scripts
├── test
├── third_party
├── tools
├── vendor
├── web
└── website
一個 Go 項目包含 3 大部分:Go 應用 、項目管理和文檔。所以,我們的項目目錄也可以分為這 3 大類。同時,Go 應用又貫穿開發階段、測試階段和部署階段,相應的應用類的目錄,又可以按開發流程分為更小的子類。所以整體來看,我們的目錄結構可以按下圖所示的方式來分類:
3.1 Go 應用開發目錄
開發的代碼包含前端代碼和后端代碼,可以分別存放在前端目錄和后端目錄中。
3.1.1 /web
前端代碼存放目錄,主要用來存放 Web 靜態資源,服務端模板和單頁應用(SPAs)。
3.1.2 /cmd
一個項目有很多組件,可以把組件 main 函數所在的文件夾統一放在 /cmd 目錄下,例如:
$ ls cmd/
gendocs geniamdocs genman genyaml apiserver iamctl iam-pump$ ls cmd/apiserver/
apiserver.go
這里要保證 /cmd/<組件名> 目錄下不要存放太多的代碼,如果你認為代碼可以導入并在其他項目中使用,那么它應該位于 /pkg 目錄中。如果代碼不是可重用的,或者你不希望其他人重用它,請將該代碼放到 /internal 目錄中。
3.1.3 /internal
存放私有應用和庫代碼。如果一些代碼,你不希望在其他應用和庫中被導入,可以將這部分代碼放在/internal 目錄下。
在引入其它項目 internal 下的包時,Go 語言會在編譯時報錯:
An import of a path containing the element “internal” is disallowed
if the importing code is outside the tree rooted at the parent of the
"internal" directory.
如果 internal 目錄下直接存放每個組件的源碼目錄(一個項目可以由一個或多個組件組成),當項目變大、組件增多時,可以將新增加的組件代碼存放到 internal 目錄,這時 internal 目錄就是可擴展的。例如:
$ ls internal/
apiserver authzserver iamctl pkg pump watcher
/internal 目錄建議包含如下目錄:
/internal/apiserver:該目錄中存放真實的應用代碼。這些應用的共享代碼存放在/internal/pkg目錄下。/internal/pkg:存放項目內可共享,項目外不共享的包。這些包提供了比較基礎、通用的功能,例如工具、錯誤碼、用戶驗證等功能。
建議是,一開始將所有的共享代碼存放在 /internal/pkg 目錄下,當該共享代碼做好了對外開發的準備后,再轉存到 /pkg 目錄下。
3.1.4 /pkg
該目錄中存放可以被外部應用使用的代碼庫,其他項目可以直接通過 import 導入這里的代碼。所以,我們在將代碼庫放入該目錄時一定要慎重。
3.1.5 /vendor
項目依賴,可通過 go mod vendor 創建。需要注意的是,如果是一個 Go 庫,不要提交 vendor 依賴包。
3.1.6 /third_party
外部幫助工具,分支代碼或其他第三方應用(例如 Swagger UI)。比如我們 fork 了一個第三方 go 包,并做了一些小的改動,我們可以放在目錄 /third_party/forked 下。一方面可以很清楚的知道該包是 fork 第三方的,另一方面又能夠方便地和 upstream 同步。
3.2 Go 應用測試目錄
3.2.1 /test
用于存放其他外部測試應用和測試數據。/test 目錄的構建方式比較靈活:對于大的項目,有一個數據子目錄是有意義的。例如,如果需要 Go 忽略該目錄中的內容,可以使用 /test/data 或 /test/testdata 目錄。
需要注意的是,Go 也會忽略以 . 或 _ 開頭的目錄或文件。這樣在命名測試數據目錄方面,可以具有更大的靈活性。
3.3 Go 應用部署目錄
3.3.1 /configs
這個目錄用來配置文件模板或默認配置。例如,可以在這里存放 confd 或 consul-template 模板文件。這里有一點要注意,配置中不能攜帶敏感信息,這些敏感信息,我們可以用占位符來替代,例如:
apiVersion: v1
user: username: ${CONFIG_USER_USERNAME} # iam 用戶名 password: ${CONFIG_USER_PASSWORD} # iam 密碼
3.3.2 /deployments
用來存放 Iaas、PaaS 系統和容器編排部署配置和模板(Docker-Compose,Kubernetes/Helm,Mesos,Terraform,Bosh)。在一些項目,特別是用 Kubernetes 部署的項目中,這個目錄可能命名為 deploy。
3.3.3 /init
存放初始化系統(systemd,upstart,sysv)和進程管理配置文件(runit,supervisord)。比如 sysemd 的 unit 文件。這類文件,在非容器化部署的項目中會用到。
3.4 Go 應用項目管理目錄
3.4.1 /Makefile
一個 Go 項目在其根目錄下應該有一個 Makefile 工具,用來對項目進行管理,Makefile 通常用來執行靜態代碼檢查、單元測試、編譯等功能。其他常見功能:
- 靜態代碼檢查(
lint):推薦用golangci-lint。 - 單元測試(
test):運行go test ./...。 - 編譯(
build):編譯源碼,支持不同的平臺,不同的CPU架構。 - 鏡像打包和發布(
image/image.push):現在的系統比較推薦用Docker/Kubernetes進行部署,所以一般也要有鏡像構建功能。 - 清理(
clean):清理臨時文件或者編譯后的產物。 - 代碼生成(
gen):比如要編譯生成protobuf pb.go文件。 - 部署(
deploy,可選):一鍵部署功能,方便測試。 - 發布(
release):發布功能,比如:發布到Docker Hub、github等。 - 幫助(
help):告訴Makefile有哪些功能,如何執行這些功能。 - 版權聲明(
add-copyright):如果是開源項目,可能需要在每個文件中添加版權頭,這可以通過 Makefile 來添加。 - API 文檔(
swagger):如果使用swagger來生成API文檔,這可以通過Makefile來生成。
建議:直接執行 make 時,執行如下各項 format -> lint -> test -> build,如果是有代碼生成的操作,還可能需要首先生成代碼 gen -> format -> lint -> test -> build。
3.4.2 /scripts
該目錄主要用來存放腳本文件,實現構建、安裝、分析等不同功能。不同項目,里面可能存放不同的文件,但通??梢钥紤]包含以下 3 個目錄:
/scripts/make-rules:用來存放makefile文件,實現/Makefile文件中的各個功能。Makefile有很多功能,為了保持它的簡潔,我建議你將各個功能的具體實現放在/scripts/make-rules文件夾下/scripts/lib:shell庫,用來存放shell腳本。一個大型項目中有很多自動化任務,比如發布、更新文檔、生成代碼等,所以要寫很多shell腳本,這些shell腳本會有一些通用功能,可以抽象成庫,存放在/scripts/lib目錄下,比如logging.sh,util.sh等。/scripts/install:如果項目支持自動化部署,可以將自動化部署腳本放在此目錄下。如果部署腳本簡單,也可以直接放在/scripts目錄下。
另外,shell 腳本中的函數名,建議采用語義化的命名方式,例如 iam::log::info 這種語義化的命名方式,可以使調用者輕松的辨別出函數的功能類別,便于函數的管理和引用。
3.4.3 /build
這里存放安裝包和持續集成相關的文件。這個目錄下有 3 個大概率會使用到的目錄,在設計目錄結構時可以考慮進去。
/build/package:存放容器(Docker)、系統(deb,rpm,pkg)的包配置和腳本。/build/ci:存放CI的配置文件和腳本。/build/docker:存放子項目各個組件的Dockerfile文件。
3.4.4 /tools
存放這個項目的支持工具。這些工具可導入來自 /pkg 和 /internal 目錄的代碼。
3.4.5 /githooks
Git 鉤子。比如,我們可以將 commit-msg 存放在該目錄。
3.4.6 /assets
項目使用的其他資源 (圖片、CSS、JavaScript 等)。
3.4.7 /website
如果你不使用 Github 頁面,則在這里放置項目的網站數據。
3.5 Go 應用文檔目錄
3.5.1 /README.md
項目的 README 文件一般包含了項目的介紹、功能、快速安裝和使用指引、詳細的文檔鏈接以及開發指引等。
3.5.2 /docs
存放設計文檔、開發文檔和用戶文檔等(除了 godoc 生成的文檔)。推薦存放以下幾個子目錄:
/docs/devel/{en-US,zh-CN}:存放開發文檔、hack 文檔等。/docs/guide/{en-US,zh-CN}: 存放用戶手冊,安裝、quickstart、產品文檔等,分為中文文檔和英文文檔。/docs/images:存放圖片文件。
3.5.3 /CONTRIBUTING.md
開源就緒的項目,用來說明如何貢獻代碼,如何開源協同等等。CONTRIBUTING.md 不僅能夠規范協同流程,還能降低第三方開發者貢獻代碼的難度。
3.5.4 /api
/api 目錄中存放的是當前項目對外提供的各種不同類型的 API 接口定義文件,其中可能包含類似 /api/protobuf-spec、/api/thrift-spec、/api/http-spec、openapi、swagger 的目錄,這些目錄包含了當前項目對外提供和依賴的所有 API 文件。
3.5.5 /LICENSE
版權文件可以是私有的,也可以是開源的。常用的開源協議有:Apache 2.0、MIT、BSD、GPL、Mozilla、LGPL。
3.5.6 /CHANGELOG
當項目有更新時,為了方便了解當前版本的更新內容或者歷史更新內容,需要將更新記錄存放到 CHANGELOG 目錄。
3.5.7 /examples
存放應用程序或者公共包的示例代碼
4. 不建議的目錄
4.1 /src
其中一個重要的原因是:在默認情況下,Go 語言的項目都會被放置到$GOPATH/src 目錄下。這個目錄中存放著所有代碼,如果我們在自己的項目中使用/src 目錄,這個包的導入路徑中就會出現兩個 src,例如:
$GOPATH/src/github.com/marmotedu/project/src/main.go
這樣的目錄結構看起來非常怪。
4.2 /xxs
在 Go 項目中,要避免使用帶復數的目錄或者包。建議統一使用單數。
5. 建議
對于小型項目,可以考慮先包含 cmd、pkg、internal 3 個目錄,其他目錄后面按需創建,例如:
$ tree --noreport -L 2 tms
tms
├── cmd
├── internal
├── pkg
└── README.md
另外,在設計目錄結構時,一些空目錄無法提交到 Git 倉庫中,但我們又想將這個空目錄上傳到 Git 倉庫中,以保留目錄結構。這時候,可以在空目錄下加一個 .keep 文件,例如:
$ ls -A build/ci/
.keep
總結
以上是生活随笔為你收集整理的Go 学习笔记(84)— Go 项目目录结构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 科大奥锐干涉法测微小量实验的数据_光学干
- 下一篇: 2022-2028年中国BOPET薄膜行