Go 应用程序相关目录
/cmd 主干目录
这个目录主要负责程序的启动、初始化、停止等功能,故主要包含项目的入口文件 main.go,如果一个项目有多个组件,则可以存放多个组件的 main.go,例如:
cmd
├── ctl
│ └── main.go
├── server
│ └── main.go
└── task
└── main.go
├── cmd
│ ├── migration
│ ├── server
│ │ ├── wire
│ │ │ ├── wire.go
│ │ │ └── wire_gen.go
│ │ └── main.go
│ └── task
不要在这个目录中放太多的代码,更不要放业务逻辑代码,保持整洁。
cmd:该模块包含了应用的入口点,根据不同的命令进行不同的操作,例如启动服务器、执行数据库迁移等。每个子模块都有一个main.go文件作为入口文件,以及wire.go和wire_gen.go文件用于依赖注入。
/internal 不允许被导入的包
该模块是应用的核心模块,包含了各种业务逻辑的实现。
不允许被其他项目导入的包
存放项目的内部私有代码和库,不允许在项目外部使用。同时这也是 Go 在编译时强制执行的校验规则,如果在其他项目中导入 internal 目录下的内容,Go(1.19) 在编译时会得到如下错误:
use of internal package xxx not allowed
注:xxx 为包含 internal 的包路径
在项目的目录树中的任意位置都可以有 internal 目录,而不仅仅是在顶级目录中。
在 /internal 内部可以增加额外的包结构来区分组件间共享和私有的内部代码:
internal
├── app
│ ├── ctl
│ ├── server
│ └── task
└── pkg
├── internal
│ ├── handler
│ ├── middleware
│ ├── model
│ ├── repository
│ ├── server
│ └── service
其中 /internal/app 下存放各个组件的逻辑代码,/internal/pkg 下存放各组件间的共享代码。
handler:该子模块包含了处理HTTP请求的处理器,负责接收请求并调用相应的服务进行处理。
job:该子模块包含了后台任务的逻辑实现。
model:该子模块包含了数据模型的定义。
repository:该子模块包含了数据访问层的实现,负责与数据库进行交互。
server:该子模块包含了HTTP服务器的实现。
service:该子模块包含了业务逻辑的实现,负责处理具体的业务操作。
internal下面可以是这样的目录
- entities
- user.go 与数据库表对应
- role.go
- repository(provider)
- user.provider.go 提供和数据库操作基本方法 CRUD 方法
- role.provider.go
- modules
- user
- user.dto.go 一些入参映射以及响应参数的对应的类
- user.handler.go 其实就是 controller
- user.service.go 核心:主要处理业务逻辑
- routers
/pkg 可以被导入的包
该模块包含了一些通用的功能和工具。
包含可导出的公共库,可以被其他项目引用。这意味着此目录下的代码可以被导入任何其他项目,被当作库程序来使用,所以将代码放到此目录前要慎重考虑,不要将私有代码放到此目录下。
Travis Jeffery 撰写了一篇文章讲解了 pkg 和 internal 目录使用建议,你可以进一步了解学习。
/configs 配置文件
此目录存放配置文件模板或默认配置。
该模块包含了应用的配置文件,根据不同的环境(如开发环境和生产环境)提供不同的配置。
前文讲设计项目目录的基本原则时提到目录名最好使用单数形式,不过由于使用 configs 来存放配置已经是约定俗成的事实标准,故此目录名称可以打破这项设计原则。
/test
该模块包含了各个模块的单元测试,按照模块划分子目录。
可以用来存放 e2e 测试和测试数据等。
对于较大的项目,有一个数据子目录更好一些。例如,如果需要 Go 在编译时忽略目录中的内容,则可以使用 /test/data 或 /test/testdata 这样的目录名称。
另外 Go 还会忽略以 . 或 _ 开头的目录或文件,因此可以更具灵活性的来命名测试数据目录。
deployments
该模块用于部署应用,包含了一些部署脚本和配置文件。
用来存放 IaaS、PaaS 系统和容器编排部署所需要的配置及模板(如:Docker-Compose,Kubernetes/Helm,Mesos,Terraform,Bosh)。
如果你的项目作为 Kubernetes 生态中的一员或使用 Kubernetes 部署,则建议命名为 /deploy,更加符合 Kubernetes 社区风格。
/third_party
外部辅助工具目录,fork 的代码和其他第三方工具(例如 Swagger UI)。比如我们修改了某个开源的第三方项目的代码,使其满足当前项目的使用需求,就可以将修改后的代码放到 /third_party/fork 目录下进行维护。
/web
该模块包含了前端相关的文件,如HTML、CSS和JavaScript等。
如果你打算在项目目录下包含配套的前端程序代码,则可以存放到此目录。主要包括静态资源、前端代码、路由等。
如果你的项目仅提供 RESTful API,且前后端程序需要分开独立维护,则可以不需要此目录,建议将前端代码作为一个独立的项目存在。
项目管理相关目录
/init
包含系统初始化(systemd、upstart、sysv)和进程管理(runit、supervisord)等配置。这在非容器化部署的项目中非常有用。
另外还可以包含初始化代码,如数据库迁移、缓存初始化等。
/scripts
该模块包含了一些脚本文件,用于项目的构建、测试和部署等操作。
存放用于执行各种构建、安装、分析等操作的脚本。
根文件 /Makefile 可以引用这些脚本,使其变得更小、更易于维护。
/build
存放程序构建和持续集成相关的文件。例如:
使用 /build/package 目录来存放云(AMI),容器(Docker),操作系统(deb,rpm,pkg)软件包配置和脚本。
使用 /build/ci 目录来存放 CI(travis、circle、drone)配置文件和脚本。
/tools
此项目的支持工具。这些工具可以从 /pkg 和 /internal 目录导入代码。
/assets
项目使用的其他资源 (Image、CSS、JavaScript、SQL 文件等)。
/githooks
Git 相关的钩子存放目录。
项目文档相关目录
/api
当前项目对外暴露的 API 文档,如 OpenAPI/Swagger 规范文档、JSON Schema 文件、ProtoBuf 定义文件等。
api
└── openapi
└── openapi.yaml
/docs
设计、开发和用户文档等(除 godoc 生成的文档)。
/examples
应用程序或公共库的示例。降低使用者的上手难度。
不建议使用的目录
/src
一些有 Java 或 Python 开发经验的开发者习惯在项目中设计一个 /src 目录,但在 Go 语言中这是不推荐的做法。
早期的 Go 语言的项目都会被放置到 $GOPATH/src 目录下,如果项目中再有一个 /src 目录,那么项目最终的存放的路径就显得比较奇怪:
bash
复制代码$GOPATH/src/your_project/src/your_code.go
因此,请不要在一个 Go 项目中设计 /src 目录。
一些放在项目根目录下的文件
/README.md
README.md 是学习并使用项目的入口,是让用户了解项目的第一手资料。一个项目的 README.md 通常包含项目名称和简介、安装说明、使用说明、贡献方式、版权和许可等。
GitHub 也会默认解析 README.md 文件并渲染成 HTML 文档。
/Makefile
Makefile 是一个老牌的项目管理工具,建议在 Go 项目中都集成它。Makefile 语法可以参考 跟我一起写 Makefile。
/CHANGELOG
用于存放项目的更新记录,如版本号、作者、更新内容等。如果嫌麻烦,还可以使用 git-chglog 或类似工具自动生成。
/CONTRIBUTING.md
如果你的项目是开源项目,则建议包含 /CONTRIBUTING.md 文件,用来说明如何贡献代码、项目规范等,让第三方开发者更容易参与进来。
/LICENSE
开源项目一定要包含 /LICENSE,即开源许可证。没有开源许可证的项目,严格来讲不叫开源项目,如何选择开源许可证可以参考我的另一篇文章 开源协议简介。
总结
经过上面的讲解,最终我们得到的项目目录结构如下:
project
├── CHANGELOG
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── api
│ └── openapi
│ └── openapi.yaml
├── assets
├── build
├── cmd
│ ├── ctl
│ │ └── main.go
│ ├── server
│ │ └── main.go
│ └── task
│ └── main.go
├── configs
├── deployments
├── docs
├── examples
├── githooks
├── init
├── internal
│ ├── app
│ │ ├── ctl
│ │ ├── server
│ │ └── task
│ └── pkg
├── pkg
├── scripts
├── test
├── third_party
├── tools
└── web
此目录结构主要参考 golang-standards/project-layout 项目,和一些我自己的思考,最终总结出来。
.
├── api
│ └── v1
├── cmd
│ ├── migration
│ ├── server
│ │ ├── wire
│ │ │ ├── wire.go
│ │ │ └── wire_gen.go
│ │ └── main.go
│ └── task
├── config
├── deploy
├── docs
├── internal
│ ├── handler
│ ├── middleware
│ ├── model
│ ├── repository
│ ├── server
│ └── service
├── pkg
├── scripts
├── test
│ ├── mocks
│ └── server
├── web
├── Makefile
├── go.mod
└── go.sum