目录
文章目录
- 目录
- 项目布局(Project Layout)规范
- 程序目录
- /cmd(Command)包
- /internal
- /pkg(Package)
- /vendor
- Service(服务端)应用目录
- /api
- Web 应用目录
- /web
- 常规目录
- /configs
- /init
- /scripts
- /build
- /deployments
- /test
- Makefile
- /docs
- /tools
- /examples
- /third_party
- /githooks
- /assets
- /website
- 不建议使用的目录
- /src
- 适用于框架或库的平铺布局
项目布局(Project Layout)规范
类似于 Python 的 Module,Golang 对于模块的划分是基于 Package 这个概念,一个文件就可以是一个 Package(通过 package 语句)。也就是说,在一个目录中可以实现多个 Packages。但实际上并不建议这样的实现方式,主要的缺点是模块之间的关系不清晰,另外也不利于模块功能扩展。所以,推荐的是将一个目录定位一个 Package,一个 Package(目录)下属存在多个代码文件。
注意,Golang 项目中,一个 Package 建议是一个目录,一个目录不一定是一个 Package。
基于此,Golang 项目的布局应该遵循一定的规范。官方标准:https://github.com/golang-standards/project-layout
注意:本文列举的目录为项目顶层子目录清单。
├── .gitignore
├── LICENSE.md
├── Makefile
├── README.md
├── go.mod
├── api
├── assets
├── build
├── cmd
├── configs
├── deployments
├── docs
├── examples
├── githooks
├── init
├── internal
├── pkg
├── scripts
├── test
├── third_party
├── tools
├── vendor
├── web
└── website
程序目录
/cmd(Command)包
存放项目下属的若干个程序的可执行文件,也就是程序入口文件(e.g. /cmd/app/main.go)。
参考:https://github.com/golang-standards/project-layout/blob/master/cmd/README.md
cmd 包下的每个程序的目录名应该和程序的可执行文件名保持一致。
通常的,程序的 main() 函数不宜编写太多松散的代码。
/internal
存放项目 “内部” 代码。即:当前项目内部(Internal)私有的程序和库代码。
放在 internal 包的代码表明只希望在项目内部使用,当我们在其他项目中的程序导入来自 internal 包的依赖时,Golang 会在编译时报错:
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/app 子目录下,而这些应用程序共享的代码可以存放在 /internal/pkg 子目录和 /pkg 中。
/pkg(Package)
存放 “公共” 代码。
所谓 “公共” 就是可以被其他的项目引用。
通常的,pkg 包的代码应该与具体的业务无关,具有高度通用性的抽象,以方便本项目和其他项目重用。其作用类似于 C 语言中的库文件。当我们使用 go install 安装项目的时候,会将代码编译成 .a 静态库文件。
当你决定将代码放入 pkg 包时,你应该对其负责,因为其他项目中的程序可以通过 import 来导入这里的代码。所以当我们将代码放入 pkg 包时一定要慎重,考虑清楚这些代码是否应该被 “公开”。
/vendor
存放程序的外部依赖。可以手工管理,也可以使用 Golang 提供的 dep 工具(https://github.com/golang/dep)或者 glide(https://github.com/Masterminds/glide)工具。
在 Go 1.13 版本启用了 go module 来代替,并不需要 /vendor 目录了。
Service(服务端)应用目录
/api
存放项目对外提供的若干个北向 API 接口的定义文件。包括:OpenAPI/Swagger 的规范(Specs)文件、JSON schema 文件,以及协议定义文件。例如: /api/protobuf-spec、/api/thrift-spec 或者 /api/http-spec 目录,这些目录中包含了当前项目对外提供的和依赖的所有 API 文件:
$ tree ./api
api
└── protobuf-spec
└── oceanbookpb
├── oceanbook.pb.go
└── oceanbook.proto
其中,创建 oceanbookpb 二级目录的主要作用在于一个项目同时提供了多种不同的访问方式时,这样可以避免潜在的冲突问题,也更加清晰。
参考:https://github.com/golang-standards/project-layout/blob/master/api/README.md
Web 应用目录
/web
存放 Web 应用的标准组件:
- 静态 Web 资源
- 服务端模板
- 单页应用
常规目录
/configs
存放配置文件的模板(e.g. consul-template)或者默认配置文件(e.g. confd)。
/init
存放系统初始化或自动启动脚本(e.g. systemd、upstart、sysv),以及存放进程管理或监控程序的配置文件(e.g. runit、supervisord)。
/scripts
存放执行各种构建、安装、分析等,或者其他操作的脚本文件。
参考:https://github.com/golang-standards/project-layout/blob/master/scripts/README.md
/build
存放打包及持续集成所需要的的文件。
- 将 cloud(AMI)、container(Docker)、OS(deb、rpm、pkg)软件包的配置放在 /build/package 目录下。
- 将 CI(travis、circle、drone)系统的配置和脚本放在 /build/ci 目录下。需要注意的是,有些 CI 工具(e.g. Travis CI)对它们的配置文件的位置非常敏感。此时,我们可以把配置文件放在 /build/ci 目录下,然后将它们软链接到 CI 工具希望它们出现的位置路径。
/deployments
存放 IaaS、PaaS、系统,以及容器编排的部署配置文件和模板文件。例如:docker-compose、kubernetes/helm、mesos、terraform、bosh 等。
/test
存放除了单元测试、基准测试之外的测试文件,例如:集成测试、测试数据等。
对于大型的项目则有必要包含一个 /test/data 子目录,Golang 会忽略其中的某些内容。例如:/test/data 或者 /test/testdata 目录。
注意,Golang 还会忽略任何以 “.” 和 “_” 开头的文件和目录。
参考:https://github.com/golang-standards/project-layout/blob/master/test/README.md
Makefile
Makefile 定义了项目编译的细节,并且 /scripts 目录下的脚本文件通常由 Makefile 触发。
/docs
存放除了使用 godc 自动生成的程序文档文件之外的文档资料,例如:用户及设计文档。
参考:https://github.com/golang-standards/project-layout/blob/master/docs/README.md
/tools
存放项目需要工具集。这些工具可以导入 /pkg 和 /internal 目录下的代码。
参考:https://github.com/golang-standards/project-layout/blob/master/tools/README.md
/examples
存放程序示例文件。
参考:https://github.com/golang-standards/project-layout/blob/master/examples/README.md
/third_party
存放第三方辅助工具、forked 代码。例如:Swagger UI。
/githooks
存放 Git books 文件。
/assets
存放其他资产(assets)文件,通常是图片、logo 等文件。
/website
如果不使用 Github Pages(Github 网页)的话,可以在 /website 放置项目的网站文件。通常是用于项目宣传和展示的网站资料。
参考:https://github.com/golang-standards/project-layout/blob/master/website/README.md
不建议使用的目录
/src
注意,这里所说的 /src 目录指的是 “项目顶层” 的 /src,而不是 “Go workspace” 的 /src 目录。
$GOPATH 环境变量指向当前环境中的 Go workspace,如果想要让代码运行,就需要将项目的代码放到 workspace 下。在 UNIX-like 操作系统中通常是 $HOME/go,笔者的配置为:
$ go env | grep PATH
GOPATH="/Users/mickeyfan/workspace/go"
这个 workspace 的顶层包括了 /pkg、/bin 和 /src 目录,Golang 项目在默认情况下都会被放置到 $GOPATH/src 目录下,存储着我们开发和依赖的全部项目代码。
所以,如果我们在实际项目的顶层定义了 /src 目录,那么该项目的 $GOPATH 中就会出现两个 src,e.g.
/some/path/to/workspace/src/your_project/src/your_code.go。
所以,不建议在项目的顶层创建 /src 子目录,区别于 JAVA 的编程习惯。
适用于框架或库的平铺布局
当我们希望在项目中导入一个采用了 pkg 布局方式的框架时,我们往往需要使用 github.com/draveness/project/pkg/somepkg 的路径;而当我们导入一个采用了平铺布局方式的框架时,只需要使用 github.com/draveness/project 路径,很明显地减少了引用依赖包语句的长度。
所以,对于一个 Golang 的框架或者库而言,将代码平铺在其根目录下也很正常,但在一个常规的 Golang 后端项目中就不是那么合适了。