好的项目结构能让代码更易理解、易维护、易扩展。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. 最佳实践

  1. 从简单开始:不要一开始就创建复杂的目录结构
  2. 按需扩展:当代码变多时再拆分目录
  3. 优先使用 internal:除非确定要公开,否则放在 internal/
  4. 保持一致:团队内统一目录结构
  5. 文档先行:在 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/ 这个特殊目录?它解决了什么问题?


相关阅读