为什么项目结构很重要
代码结构是团队协作的隐形成本。好的结构让人一眼看懂模块边界,差的结构让改一个 bug 需要改七个文件。随着项目增长,重新组织结构的代价会越来越高——所以从第一天就把结构做好。
一种实用的 Go 项目分层
myapp/
├── cmd/
│ └── server/
│ └── main.go # 入口,只负责 wire 和启动
├── internal/
│ ├── handler/ # HTTP/gRPC handler(接收请求)
│ ├── service/ # 业务逻辑层(纯函数,无依赖注入简化测试)
│ ├── repo/ # 数据访问层(DB / Cache / 外部 API)
│ └── model/ # 数据模型(贫血模型,纯数据结构)
├── pkg/
│ └── response/ # 对外暴露的公共库
├── migrations/ # 数据库迁移文件
├── config/
│ └── config.go # 配置加载
├── Makefile
└── go.mod
各层职责定义
Handler 层负责解析请求参数、做基础校验、调用 Service 层、组装响应。注意:Handler 不写任何业务逻辑。
Service 层包含核心业务逻辑。推荐用纯函数风格,依赖通过参数传入,方便写单元测试。
Repo 层封装所有数据访问,只对 Service 层暴露存储抽象——换数据库、换缓存策略,不影响上层代码。
避免几个常见反模式
- 把 model 当贫血模型塞进 repo 层,导致 repo 越来越胖
- 在 handler 里直接写 SQL,耦合数据库细节
- 全局变量(var db *sql.DB)跨包共享,测试时无法 mock
- 把所有类型都放在 model 包,没有按领域聚合
小结
Go 的项目结构没有标准答案,但有一个原则:让每一层都专注于一件事。如果你的 handler 里有 SQL,你的 service 里在 new 各种客户端,那是结构在提醒你需要调整了。
