Go语言项目布局
参考
Golang项目布局开源项目-29.1K Stars
《Go语言精进之路》第二部分
GopherCon 2018: Kat Zien - How Do You Structure Your Go Apps
1. 前言
在项目中,合理的布局会让项目更加清晰。随着项目的增长,保持良好的项目结构非常重要,否则最终会得到一团混乱的代码。为了便于开发者协作,最好使用通用的结构,下面这个开源项目中介绍了 Go 语言项目布局的最佳实践,目前有接近 3 万个 star,对我后续的开发还是有一定参考价值的。
https://github.com/golang-standards/project-layout
从 Go 1.14开始,建议Go项目使用Go Modules 进行依赖管理,用 go.mod 来记录特定的依赖信息。
2. Go语言自身结构
作为 Go 语言的创世项目,Go 的项目结构的布局对后续的 Go 语言项目具有重要的参考意义。下面的结构基于 Go 1.16
$ tree -LF 1 /usr/local/go
/usr/local/go
├── AUTHORS
├── CONTRIBUTING.md
├── CONTRIBUTORS
├── LICENSE
├── PATENTS
├── README.md
├── SECURITY.md
├── VERSION
├── api/
├── bin/
├── doc/
├── favicon.ico
├── lib/
├── misc/
├── pkg/
├── robots.txt
├── src/
└── test/
尤其是早期 Go 项目中 src目录下面的结构,更是在后续被 Go 社区作为 Go 应用项目结构的模板广泛使用。
- 代码构建的脚本源文件放在
src下面的顶层目录下。 src下的二级目录cmd下面存放着 Go 工具链相关的可执行文件(比如go、gofmt等)的主目录以及它们的main包源文件。src存放着上面cmd下各工具链程序依赖的包、Go 运行时以及 Go 标准库的源文件。src下面的二级目录internal,用于存放无法被外部导入、仅 Go 项目自用的包。src下面的go.mod和go.sum,实现了 Go 项目自身的go module迁移。Go项目内所有包被放到名为std的module下面,其依赖的包依然是golang.org/x下的各个包。
$ tree -LF 1 /usr/local/go/src
/usr/local/go/src
├── Make.dist
├── README.vendor
├── all.bash*
├── all.bat
├── all.rc*
├── archive/
├── bootstrap.bash*
├── bufio/
├── buildall.bash*
├── builtin/
├── bytes/
├── clean.bash*
├── clean.bat
├── clean.rc*
├── cmd/
├── cmp.bash
├── compress/
├── container/
├── context/
├── crypto/
├── database/
├── debug/
├── embed/
├── encoding/
├── errors/
├── expvar/
├── flag/
├── fmt/
├── go/
├── go.mod
├── go.sum
├── hash/
├── html/
├── image/
├── index/
├── internal/
├── io/
├── log/
├── make.bash*
├── make.bat
├── make.rc*
├── math/
├── mime/
├── net/
├── os/
├── path/
├── plugin/
├── race.bash*
├── race.bat
├── reflect/
├── regexp/
├── run.bash*
├── run.bat
├── run.rc*
├── runtime/
├── sort/
├── strconv/
├── strings/
├── sync/
├── syscall/
├── testdata/
├── testing/
├── text/
├── time/
├── unicode/
├── unsafe/
└── vendor/
3. 以构建二进制可执行文件为目的的 Go 项目结构
下图是一个支持(在 cmd下)构建二进制可执行文件的典型 Go 项目的结构。
cmd目录:项目的主干,存放项目要构建的可执行文件对应的main包的源文件。如果有多个可执行文件需要构建,则将每个可执行文件的main包单独放在一个子目录中,比如图中的app1、app2。一些Go项目将cmd这个名字改为app,但其功能并没有变。
通常有一个小的main函数,从/internal和/pkg目录导入和调用代码。不要在这个目录中放置太多代码。如果你认为代码可以导入并在其他项目中使用,那么它应该位于/pkg目录中。如果代码不是可重用的,或者你不希望其他人重用它,请将该代码放到/internal目录中。pkg目录:该目录下的包可以被外部项目引用,算是项目导出包的一个聚合。对于一些规模稍大的项目,过多的包会让项目顶层目录不再简洁,显得很拥挤,因此对于复杂的 Go 项目建议保留pkg目录。internal目录:对于不想暴露给外部引用,仅限项目内部使用的包,在项目结构上可以通过 Go 1.4 版本中引入的internal包机制来实现。go.mod和go.sum:Go 语言包依赖管理使用的配置文件。Makefile:项目构建工具所用脚本的“代表”,一般会放在build目录下。
4. 通用应用目录
configs目录:配置文件模板或默认配置。init目录:系统初始化文件目录。scripts目录:执行各种构建、安装、分析等操作的脚本。build目录:打包和持续集成。将你的云( AMI )、容器( Docker )、操作系统( deb、rpm、pkg )包配置和脚本放在/build/package目录下。将你的 CI (travis、circle、drone)配置和脚本放在/build/ci目录中。test目录:额外的外部测试应用程序和测试数据。
5. 其他目录
docs目录:设计和用户文档(除了godoc生成的文档之外)。tools目录:这个项目的支持工具。注意,这些工具可以从/pkg和/internal目录导入代码。examples目录:应用程序和/或公共库的示例。third_party目录:外部辅助工具,分叉代码和其他第三方工具(例如 Swagger UI)。
可以根据项目需求合理添加其他目录结构。
注意:有些 Go 项目确实有一个 src 文件夹,但这通常发生在开发人员有 Java 背景,在那里它是一种常见的模式。如果可以的话,尽量不要采用这种 Java 模式。你真的不希望你的 Go 代码或 Go 项目看起来像 Java