Go 单体仓库:面向可扩展项目的代码组织
在现代软件开发中,如何组织代码使其保持可管理、可复用和可扩展,这个问题越来越常见。一个流行的解决方案是使用 单体仓库 (monorepo) —— 一个包含多个项目或服务的单一仓库。在本文中,我们将探讨如何用 Go (Golang) 语言高效地组织单体仓库,介绍最佳实践、目录结构、模块管理以及 CI/CD 配置。
什么是单体仓库,为什么 Go 需要它?
单体仓库是一种将公司或大型项目的所有代码存储在一个仓库中的方法。它可以包括:
- 多个微服务
- 库和工具
- 配置和脚本
- 文档
Go 单体仓库的优势:
- 统一的依赖版本 —— 消除服务间的版本冲突。
- 简化重构 —— 公共库的更改对所有使用者立即可见。
- 轻松复用代码 —— 无需将包发布到注册表。
- 统一的提交历史 —— 简化调试和审计。
然而,单体仓库需要纪律:没有清晰的结构,它会迅速变成"垃圾场"。Go 凭借其严格的模块系统 (Go Modules),为组织此类仓库提供了出色的工具。
Go 单体仓库的目录结构
正确的结构是成功的基础。我们来看看 Go 单体仓库的推荐组织方式:
my-monorepo/├── apps/ # 应用程序(服务、CLI 工具)│ ├── api-gateway/│ │ ├── cmd/│ │ │ └── main.go│ │ ├── internal/│ │ ├── go.mod│ │ └── go.sum│ ├── auth-service/│ │ └── ...│ └── user-service/│ └── ...├── libs/ # 公共库│ ├── common/│ │ ├── logger.go│ │ ├── config.go│ │ ├── go.mod│ │ └── go.sum│ └── models/│ ├── user.go│ ├── go.mod│ └── go.sum├── scripts/ # 构建、部署脚本│ ├── build.sh│ └── deploy.sh├── docs/ # 文档├── .github/ # CI/CD 配置│ └── workflows/│ └── ci.yml├── go.work # Workspace 文件 (Go 1.18+)├── Makefile # 通用命令├── README.md└── .gitignore结构的关键元素:
- apps/ —— 包含可执行应用程序(服务、工具)。每个应用程序都是一个独立的 Go 模块,拥有自己的
go.mod。 - libs/ —— 在应用程序之间复用的公共库。
- go.work —— workspace 文件,允许在本地处理所有模块而无需发布更改。
在单体仓库中使用 Go Modules
从 Go 1.18 开始,支持 workspaces (go.work)。这解决了模块相互依赖时的本地开发问题。我们来看一个配置示例。
在单体仓库根目录创建 go.work 文件:
go 1.21
use ( ./apps/api-gateway ./apps/auth-service ./libs/common ./libs/models)现在,在本地开发时,Go 将使用模块的本地版本,而不是已发布的版本。这对于快速迭代至关重要。
apps/auth-service/go.mod 中的依赖示例:
module github.com/company/auth-service
go 1.21
require ( github.com/company/libs/common v0.0.0 github.com/company/libs/model