项目的目录结构 #
一个好的目录结构设计应该是易维护、易扩展的。至少要满足以下几个要求:
- 命名清晰:目录命名要清晰、简洁,能清晰地表达出该目录实现的功能,并且目录名最好用单数。单数足以说明这个目录的功能,避免单复混用。
- 功能明确:一个目录所要实现的功能应该是明确的、并且在整个项目目录中具有很高的辨识度。当需要新增一个功能时,能够非常清楚地知道把这个功能放在哪个目录下。
- 全面性:目录结构应该尽可能全面地包含研发过程中需要的功能,例如文档、脚本、源码管理、API 实现、工具、第三方包、测试、编译产物等。
- 可预测性:项目规模一定是从小到大的,所以一个好的目录结构应该能够在项目变大时,仍然保持之前的目录结构。
- 可扩展性:每个目录下存放了同类的功能,在项目变大时,这些目录应该可以存放更多同类功能。
根据项目的功能,目录结构可以分为两种:
- 平铺式目录结构
- 结构化目录结构
平铺式目录结构 #
当一个项目是一个工具库时,适合使用平铺式目录结构。项目的代码都存放在项目的根目录下,可以减少项目引用路径的长度。例如 github.com/golang/glog
:
$ ls glog/
glog_file.go glog_flags.go glog.go glog_test.go go.mod go.sum LICENSE README
结构化目录结构 #
当一个项目是一个应用时,适合使用结构化目录结构。目前 Go 社区比较推荐的结构化目录结构是 project-layout。
下面是一套结合 project-layout 总结出的目录结构:
├── api # 存放不同类型的 API 定义文件
│ └── swagger # Swagger API 文档
├── cmd # cmd 下可以包含多个组件目录,组件目录下存放各个组件的 main 包
│ └── apiserver
│ └── apiserver.go
├── chart # helm chart 文件
├── conf # 项目部署的配置文件
├── docs # 项目文档
│ ├── dev
│ │ ├── en-US
│ │ └── zh-CN
│ ├── guide
│ │ ├── en-US
│ │ └── zh-CN
│ └── README.md
├── examples # 项目使用示例
├── go.mod
├── go.sum
├── hack # 项目构建,持续集成相关的文件
│ ├── include # 存放 makefile 文件,实现入口 Makefile 文件中的各个功能
│ ├── scripts # 存放 Shell 脚本
│ ├── docker # 包含多个组件目录,组件目录下存放各个组件的 Dockerfile,Docker Compose 文件等
│ │ └── apiserver
│ │ └── Dockerfile
│ │
├── internal # internal 下可以包含多个组件目录,组件目录下存放各个组件的业务代码
│ ├── apiserver # 组件的业务逻辑代码
│ │ ├── apiserver.go # 组件应用的入口文件
│ │ ├── config # 根据 options 创建组件应用的配置
│ │ ├── controller # HTTP API 的实现,包含请求参数的解析、校验、返回响应,具体的业务逻辑在 service 目录下
│ │ │ └── v1 # API 的 v1 版本
│ │ │ └── user
│ │ ├── options # 组件的命令行选项,可以 internal/pkg/options 中的命令行选项
│ │ ├── service # 具体的业务逻辑
│ │ │ └── v1 # v1 版本
│ │ │ └── user
│ │ ├── store # 数据库操作的代码,可以创建多个目录,对应不同的数据库
│ │ │ ├── mysql
│ │ │ │ ├── mysql.go
│ │ │ │ └── user
│ │ │ └── fake
│ │ │
│ ├── pkg # 仅项目内可用的工具包
│ │ ├── code # 项目内共享的错误码
│ │ ├── options # 项目内共享的命令行选项
│ │ └── util
├── LICENSE
├── Makefile # Makefile 入口文件
├── pkg # 全局可用的工具包,可以被外部引用
│ └── util
├── README.md
├── test # 存放测试代码
│ ├── testdata # 测试数据
│ └── e2e # e2e 测试代码