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