好的项目结构能让代码更易理解、易维护、易扩展。Go 社区有一套被广泛认可的标准布局。
1. 标准项目布局
参考:golang-standards/project-layout
1myproject/
2├── cmd/ # 主程序入口
3│ ├── server/
4│ │ └── main.go # 服务端入口
5│ └── cli/
6│ └── main.go # 命令行工具入口
7├── internal/ # 私有代码(不可被外部导入)
8│ ├── handler/
9│ ├── service/
10│ └── repository/
11├── pkg/ # 公共库(可被外部导入)
12│ ├── util/
13│ └── validator/
14├── api/ # API 定义(OpenAPI/Swagger)
15│ └── openapi.yaml
16├── web/ # Web 静态资源
17│ ├── static/
18│ └── templates/
19├── configs/ # 配置文件
20│ ├── config.yaml
21│ └── config.prod.yaml
22├── scripts/ # 脚本(构建、部署等)
23│ ├── build.sh
24│ └── deploy.sh
25├── test/ # 额外的测试数据
26│ └── fixtures/
27├── docs/ # 文档
28│ └── API.md
29├── go.mod
30├── go.sum
31├── Makefile # 构建脚本
32├── Dockerfile
33└── README.md
2. 核心目录详解
2.1 cmd/
存放主程序入口,每个子目录对应一个可执行文件。
1cmd/
2├── server/
3│ └── main.go # go build -o server ./cmd/server
4├── worker/
5│ └── main.go # go build -o worker ./cmd/worker
6└── migrate/
7 └── main.go # go build -o migrate ./cmd/migrate
main.go 应该尽量简洁:
1// cmd/server/main.go
2package main
3
4import (
5 "myproject/internal/server"
6)
7
8func main() {
9 server.Run() // 具体逻辑在 internal/server 中
10}
2.2 internal/
私有代码,只能被本项目导入,外部项目无法导入。
1internal/
2├── handler/ # HTTP 处理器
3├── service/ # 业务逻辑
4├── repository/ # 数据访问
5├── model/ # 数据模型
6└── middleware/ # 中间件
为什么需要 internal?
- 防止外部项目依赖你的内部实现
- 给你重构的自由(不用担心破坏外部依赖)
2.3 pkg/
公共库,可以被外部项目导入。
1pkg/
2├── util/ # 通用工具函数
3├── validator/ # 验证器
4└── logger/ # 日志封装
何时使用 pkg?
- 代码足够通用,可以被其他项目复用
- 已经稳定,不会频繁变动
注意:很多项目不需要 pkg/,所有代码都放在 internal/ 即可。
2.4 api/
存放 API 定义文件:
- OpenAPI/Swagger 规范
- Protocol Buffers (.proto 文件)
- GraphQL schema
1api/
2├── openapi.yaml
3├── user.proto
4└── schema.graphql
2.5 configs/
配置文件模板(不包含敏感信息)。
1configs/
2├── config.yaml # 开发环境配置
3├── config.prod.yaml # 生产环境配置模板
4└── config.example.yaml # 配置示例
敏感信息应该通过环境变量传入。
3. 其他常见目录
3.1 build/
构建相关文件:
1build/
2├── package/ # 打包脚本
3└── ci/ # CI 配置
3.2 deployments/
部署配置:
1deployments/
2├── docker-compose.yml
3├── kubernetes/
4│ ├── deployment.yaml
5│ └── service.yaml
6└── terraform/
3.3 vendor/
依赖的副本(可选):
1go mod vendor
通常不需要提交到版本控制。
4. 小型项目结构
对于简单项目,可以简化:
1simple-project/
2├── main.go # 单文件入口
3├── handler.go # HTTP 处理器
4├── model.go # 数据模型
5├── go.mod
6└── README.md
或者:
1simple-project/
2├── cmd/
3│ └── server/
4│ └── main.go
5├── internal/
6│ ├── handler/
7│ ├── service/
8│ └── model/
9├── go.mod
10└── README.md
5. 中型项目结构
1medium-project/
2├── cmd/
3│ └── server/
4│ └── main.go
5├── internal/
6│ ├── handler/
7│ │ ├── user.go
8│ │ └── post.go
9│ ├── service/
10│ │ ├── user.go
11│ │ └── post.go
12│ ├── repository/
13│ │ ├── user.go
14│ │ └── post.go
15│ ├── model/
16│ │ └── model.go
17│ └── middleware/
18│ └── auth.go
19├── pkg/
20│ └── response/
21│ └── response.go
22├── configs/
23│ └── config.yaml
24├── go.mod
25├── Dockerfile
26└── README.md
6. 大型项目结构(微服务)
1large-project/
2├── services/
3│ ├── user-service/
4│ │ ├── cmd/
5│ │ ├── internal/
6│ │ └── go.mod
7│ ├── order-service/
8│ │ ├── cmd/
9│ │ ├── internal/
10│ │ └── go.mod
11│ └── payment-service/
12│ ├── cmd/
13│ ├── internal/
14│ └── go.mod
15├── pkg/ # 跨服务共享的代码
16│ ├── logger/
17│ └── config/
18└── api/ # API 定义
19 └── proto/
7. 最佳实践
- 从简单开始:不要一开始就创建复杂的目录结构
- 按需扩展:当代码变多时再拆分目录
- 优先使用 internal:除非确定要公开,否则放在
internal/ - 保持一致:团队内统一目录结构
- 文档先行:在 README 中说明项目结构
8. 常见错误
8.1 过度设计
1❌ 不好:一开始就创建大量空目录
2myproject/
3├── cmd/
4├── internal/
5├── pkg/
6├── api/
7├── web/
8├── configs/
9├── scripts/
10├── test/
11└── docs/
1✅ 好:从简单开始
2myproject/
3├── main.go
4├── handler.go
5└── go.mod
8.2 混淆 internal 和 pkg
internal/:私有代码,频繁变动pkg/:公共库,稳定可复用
大多数项目只需要 internal/。
8.3 main.go 太复杂
1// ❌ 不好:main.go 包含大量业务逻辑
2func main() {
3 db := connectDB()
4 router := setupRouter()
5 // ... 100 行代码
6}
7
8// ✅ 好:main.go 只负责启动
9func main() {
10 server.Run()
11}
9. 总结
- cmd/:程序入口
- internal/:私有代码(最常用)
- pkg/:公共库(谨慎使用)
- configs/:配置模板
- 从简单开始,按需扩展
思考题:
为什么 Go 语言要设计 internal/ 这个特殊目录?它解决了什么问题?