Go 单体仓库:代码组织的实践指南
单体仓库 (monorepo) 是一种代码管理方法,将所有项目、库和服务存储在一个仓库中。在 Go 社区中,这种方法因其简单性和高效性而越来越受欢迎。本文将深入探讨如何在实际中组织 Go 单体仓库项目:从结构选择到 CI/CD 配置。
为什么选择 Go 单体仓库?
Go 最初就是为大型代码库设计的语言。像 go mod 和 go work 这样的工具使得单体仓库的使用变得自然。主要优势包括:
- 简化依赖管理:所有模块使用统一版本的包,不存在版本冲突。
- 统一的 CI/CD:所有服务共享一个配置文件,更易于维护。
- 重构便利:库中 API 的更改会立即反映在仓库中的所有使用者身上。
- 统一标准:代码检查工具、格式化工具和代码审查适用于所有代码。
单体仓库结构:经过验证的方案
我们来看一个非常适合中型 Go 项目的结构。假设我们有两个服务:auth-service 和 api-gateway,以及一些公共库。
my-project/├── apps/ # 可执行应用程序│ ├── auth-service/│ │ ├── cmd/│ │ │ └── main.go│ │ ├── internal/│ │ │ ├── handler/│ │ │ ├── service/│ │ │ └── repository/│ │ ├── go.mod│ │ └── go.sum│ └── api-gateway/│ ├── cmd/│ │ └── main.go│ ├── internal/│ │ ├── middleware/│ │ └── routes/│ ├── go.mod│ └── go.sum├── libs/ # 公共库│ ├── config/│ │ ├── config.go│ │ ├── go.mod│ │ └── go.sum│ ├── logger/│ │ ├── logger.go│ │ ├── go.mod│ │ └── go.sum│ └── db/│ ├── postgres.go│ ├── go.mod│ └── go.sum├── scripts/ # CI/CD 和工具脚本│ ├── build.sh│ └── test.sh├── go.work # Go Workspace (可选)├── .golangci.yml # 代码检查工具配置├── Makefile # 通用命令└── README.md结构的关键元素
apps/— 所有可执行应用程序。每个应用程序都有自己的入口点 (cmd/main.go) 和独立的go.mod。libs/— 通用库。每个库都是一个独立的 Go 模块。scripts/— 用于构建、测试和部署的辅助脚本。go.work— Go Workspace 文件,用于简化本地开发。
配置 Go Workspace 用于本地开发
Go 1.18+ 通过 go.work 文件支持 Workspace。这允许在本地引用 libs/ 中的库,而无需发布到远程仓库。
在项目根目录创建 go.work:
go 1.22
use ( ./apps/auth-service ./apps/api-gateway ./libs/config ./libs/logger ./libs/db)现在,在开发过程中,您可以直接导入库:
// apps/auth-service/cmd/main.gopackage main
import ( "my-project/libs/config" "my-project/libs/logger")
func main() { cfg := config.Load() log := logger.New(cfg.LogLevel) log.Info("Auth service started")}重要提示:在每个应用程序的 go.mod 中,对库的依赖必须通过 replace 指向本地路径:
// apps/auth-service/go.modmodule github.com/company/my-project/a