项目结构
问题
Go 项目应该怎么组织目录结构?
答案
标准项目布局
myproject/
├── cmd/ # 可执行文件入口
│ ├── server/
│ │ └── main.go # HTTP/gRPC 服务
│ └── worker/
│ └── main.go # 后台任务
├── internal/ # 私有包(不可被外部导入)
│ ├── handler/ # HTTP 处理器
│ ├── service/ # 业务逻辑
│ ├── repository/ # 数据访问
│ └── model/ # 数据模型
├── pkg/ # 可被外部导入的公共包
│ └── util/
├── api/ # API 定义(proto、OpenAPI)
│ └── proto/
├── configs/ # 配置文件
├── scripts/ # 脚本
├── deployments/ # 部署配置(Dockerfile、K8s)
├── docs/ # 文档
├── test/ # 集成测试
├── go.mod
├── go.sum
├── Makefile
└── README.md
关键目录说明
| 目录 | 说明 |
|---|---|
cmd/ | 每个子目录一个 main.go,代码尽量少,调用 internal |
internal/ | Go 编译器强制:外部模块无法导入此目录下的包 |
pkg/ | 可选,放可复用的公共包 |
api/ | Protobuf、OpenAPI、GraphQL Schema |
分层架构
// internal/handler/user.go — 接收请求,调用 Service
type UserHandler struct {
svc *service.UserService
}
func (h *UserHandler) GetUser(c *gin.Context) {
user, err := h.svc.GetByID(c, c.Param("id"))
// ...
}
// internal/service/user.go — 业务逻辑
type UserService struct {
repo repository.UserRepository
}
func (s *UserService) GetByID(ctx context.Context, id string) (*model.User, error) {
return s.repo.FindByID(ctx, id)
}
// internal/repository/user.go — 数据访问
type UserRepository interface {
FindByID(ctx context.Context, id string) (*model.User, error)
}
小项目简化
不是所有项目都需要完整分层。小项目可以更扁平:
myapp/
├── main.go
├── handler.go
├── service.go
├── model.go
├── go.mod
└── Makefile
原则
- 小项目:先扁平化,长大了再分层
- 大项目:按业务域分包(
user/、order/),而非按类型分(handler/、service/)
常见面试问题
Q1: internal/ 和 pkg/ 的区别?
答案:
internal/:Go 编译器强制私有,外部模块无法 importpkg/:约定俗成的公共包目录,任何人都可以 import
Q2: Go 项目需要 DDD 吗?
答案:看项目规模:
- 小项目:Handler → Service → Repository 三层足够
- 复杂业务:DDD 的 Domain Layer 可以更好地组织业务逻辑
- Go 社区没有 Spring 那样的 DDD 框架,通常手动组织