Go 单仓库:代码组织实践指南
\n\n单仓库(monorepo)是一种源代码管理方法,将所有项目、库和服务存储在一个仓库中。在 Go 世界中,由于内置的模块支持和灵活的包系统,这种方法变得特别流行。在本文中,我们将探讨如何在实际中组织 Go 单仓库,介绍典型结构、代码示例和最佳实践。
\n\n为什么在 Go 中需要单仓库?
\n\n单仓库解决了几个关键问题:
\n- \n
- 简化依赖管理 — 所有内部包都在一个地方,易于更新和重构。 \n
- 统一的变更历史 — 原子提交可以同时影响多个服务,无需额外同步。 \n
- 加速 CI/CD — 可以配置只构建项目中已更改部分的流水线。 \n
- 提高代码质量 — 更容易实施统一标准、代码检查工具和代码审查。 \n
Go 单仓库结构:基本方案
\n\n让我们看看 Go 单仓库的典型结构。以包含两个微服务(api 和 worker)以及一个公共库 lib 的项目为例:
my-monorepo/\n├── go.work\n├── go.work.sum\n├── apps/\n│ ├── api/\n│ │ ├── go.mod\n│ │ ├── main.go\n│ │ └── handler.go\n│ └── worker/\n│ ├── go.mod\n│ ├── main.go\n│ └── process.go\n├── lib/\n│ ├── go.mod\n│ ├── auth/\n│ │ └── auth.go\n│ └── db/\n│ └── db.go\n├── scripts/\n│ ├── build.sh\n│ └── test.sh\n└── Makefile\n\n关键元素:
\n- \n
go.work— Go 工作区文件(Workspace),用于关联所有模块。 \n apps/— 应用程序目录(微服务、CLI 工具等)。 \n lib/— 所有应用程序使用的公共库。 \n scripts/— 用于构建、测试和部署的辅助脚本。 \n
配置 Go Workspace (go.work)
\n\nGo 1.18+ 提供了 workspace 机制,允许同时处理多个模块。在根目录创建 go.work 文件:
go 1.22\n\nuse (\n ./apps/api\n ./apps/worker\n ./lib\n)\n\n现在,在开发过程中可以从项目根目录运行命令,Go 会将所有模块视为一个整体。例如,运行所有模块的测试:
\n\ngo test ./...\n\n要构建特定服务:
\n\ncd apps/api && go build -o bin/api .\n\n代码示例:公共库和微服务
\n\n在 lib/auth/auth.go 中创建一个简单的身份验证库:
package auth\n\nimport "errors"\n\nvar (\n ErrInvalidToken = errors.New("invalid token")\n)\n\n// ValidateToken 验证令牌并返回用户 ID。\nfunc ValidateToken(token string) (string, error) {\n if token == "" {\n return "", ErrInvalidToken\n }\n // 这里可以是真实的 JWT 验证或调用外部服务\n return "user-123", nil\n}\n\n现在在微服务 apps/api/handler.go 中使用这个库:
package main\n\nimport (\n "encoding/json"\n "net/http"\n "my-monorepo/lib/auth"\n)\n\ntype UserResponse struct {\n UserID string `json:"user_id"`\n}\n\nfunc UserHandler(w http.ResponseWriter, r *http.Request) {\n token := r.Header.Get("Authorization")\n userID, err := auth.ValidateToken(token)\n if err != nil {\n http.Error(w, "Unauthorized", http.StatusUnauthorized)\n return\n }\n\n resp := UserResponse{UserID: user