406 lines
17 KiB
Markdown
406 lines
17 KiB
Markdown
# Go Monorepo 目录结构设计
|
||
适配小团队开发,满足**业务独立部署、发布互不影响**,基于`docker compose`实现一键发布,遵循**轻量设计、无过度封装**原则,整体结构兼顾可维护性和开发效率,各业务模块解耦且资源可复用。
|
||
|
||
## 核心设计思路
|
||
1. **单仓库多业务**:所有代码归拢在一个monorepo,团队协作更便捷;
|
||
2. **业务模块独立**:每个业务单独放在`services/`下,拥有独立的Go代码、配置、Dockerfile、docker-compose子配置,做到独立构建/部署/发布;
|
||
3. **公共资源复用**:抽离公共库、工具、中间件配置,避免重复开发,公共代码变更需做兼容性保证(不影响各业务);
|
||
4. **根目录一键部署**:通过根目录`docker-compose.yml`聚合所有业务,支持**全局启动**或**单独启动某一个业务**,满足团队开发/生产不同场景。
|
||
|
||
---
|
||
|
||
# 最终目录结构
|
||
```
|
||
go-monorepo/
|
||
├── Makefile # 全局快捷命令(构建、启动、停止、日志等,简化团队操作)
|
||
├── docker-compose.yml # 根级compose,聚合所有业务,支持独立启动单个服务
|
||
├── .env # 全局环境变量(数据库、Redis等公共中间件地址,团队统一配置)
|
||
├── .gitignore # git忽略规则(go编译产物、docker日志、.env等)
|
||
├── go.work # Go 1.18+ 工作区文件,管理多模块,实现monorepo依赖管理
|
||
├── go.work.sum # 工作区依赖校验文件
|
||
├── common/ # 公共复用模块(所有业务均可依赖,团队统一维护,保证通用性)
|
||
│ ├── go.mod # 公共模块的mod文件(模块名:go-monorepo/common)
|
||
│ ├── go.sum
|
||
│ ├── db/ # 公共数据库封装(PostgresSQL连接、基础CRUD,轻量无过度封装)
|
||
│ │ ├── postgres.go
|
||
│ │ └── options.go
|
||
│ ├── redis/ # 公共Redis封装(连接池、基础操作)
|
||
│ │ └── redis.go
|
||
│ ├── logger/ # 公共日志(zap轻量封装,统一日志格式)
|
||
│ │ └── logger.go
|
||
│ ├── utils/ # 公共工具函数(加密、时间、字符串等)
|
||
│ │ └── common.go
|
||
│ └── middleware/ # 公共中间件(HTTP跨域、限流、日志,Gin适配)
|
||
│ └── cors.go
|
||
├── services/ # 业务服务根目录(每个子目录是一个独立业务,可单独部署)
|
||
│ ├── user/ # 业务1:用户服务(示例,团队可按业务拆分,如order/pay/user)
|
||
│ │ ├── go.mod # 独立mod文件(依赖根目录common,模块名:go-monorepo/services/user)
|
||
│ │ ├── go.sum
|
||
│ │ ├── main.go # 服务入口(Gin/GRPC均可,示例用Gin)
|
||
│ │ ├── conf/ # 业务独立配置(可覆盖全局.env,支持多环境)
|
||
│ │ │ └── config.go
|
||
│ │ ├── api/ # 接口层(HTTP路由、请求响应体)
|
||
│ │ │ └── user_api.go
|
||
│ │ ├── service/ # 业务逻辑层
|
||
│ │ │ └── user_service.go
|
||
│ │ ├── dao/ # 数据访问层(操作PostgresSQL,依赖common/db)
|
||
│ │ │ └── user_dao.go
|
||
│ │ ├── model/ # 数据模型(数据库表结构、结构体)
|
||
│ │ │ └── user_model.go
|
||
│ │ ├── Dockerfile # 业务独立Dockerfile(单独构建镜像,不影响其他业务)
|
||
│ │ └── docker-compose.yml # 业务独立compose(单独部署时使用,仅包含自身+依赖的中间件)
|
||
│ ├── order/ # 业务2:订单服务(和user服务结构完全一致,独立部署)
|
||
│ │ ├── [结构同user服务]
|
||
│ └── pay/ # 业务3:支付服务(团队可一人负责一个核心业务,完美适配)
|
||
│ ├── [结构同user服务]
|
||
├── deploy/ # 部署相关资源(非必须,可放公共配置、初始化脚本)
|
||
│ ├── postgres/ # PostgresSQL初始化(建库、建表、初始化数据脚本)
|
||
│ │ └── init.sql
|
||
│ └── redis/ # Redis初始化(可选,配置文件、持久化脚本)
|
||
│ └── redis.conf
|
||
└── logs/ # 全局日志目录(docker挂载,所有服务日志统一存放,便于排查)
|
||
└── .gitkeep
|
||
```
|
||
|
||
---
|
||
|
||
# 关键文件说明(核心实现,保证独立部署+复用)
|
||
## 1. Go工作区文件 `go.work`(monorepo核心)
|
||
Go 1.18+ 引入的**工作区模式**是实现monorepo的关键,无需将common发布到私服,本地即可实现多模块依赖,且每个业务模块独立管理自身依赖。
|
||
**go.work 内容**:
|
||
```go
|
||
go 1.21 // 建议使用稳定版Go,团队统一版本
|
||
|
||
// 加入公共模块
|
||
use ./common
|
||
|
||
// 加入所有业务服务模块(新增业务时只需追加一行)
|
||
use ./services/user
|
||
use ./services/order
|
||
use ./services/pay
|
||
```
|
||
**作用**:所有模块在同一个工作区,业务服务可直接通过`import "go-monorepo/common/xxx"`依赖公共库,本地开发无需手动`go mod replace`,团队协作无依赖问题。
|
||
|
||
## 2. 公共模块 `common/go.mod`(轻量封装,无过度设计)
|
||
仅声明公共依赖,暴露简单接口,不做复杂业务封装,示例:
|
||
```go
|
||
module go-monorepo/common
|
||
|
||
go 1.21
|
||
|
||
require (
|
||
github.com/gin-gonic/gin v1.9.1
|
||
github.com/jmoiron/sqlx v1.3.5
|
||
github.com/redis/go-redis/v9 v9.3.0
|
||
go.uber.org/zap v1.26.0
|
||
github.com/lib/pq v1.10.9 // PostgresSQL驱动
|
||
)
|
||
|
||
require (
|
||
// 间接依赖(go mod tidy 自动生成)
|
||
)
|
||
```
|
||
|
||
## 3. 业务模块 `services/user/go.mod`(独立依赖,发布不影响其他业务)
|
||
业务模块仅依赖自身需要的包+公共模块,**每个业务的mod独立管理**,升级依赖/修改代码不会影响其他业务,示例:
|
||
```go
|
||
module go-monorepo/services/user
|
||
|
||
go 1.21
|
||
|
||
// 依赖本地公共模块(工作区模式下,无需指定版本)
|
||
require go-monorepo/common v0.0.0-00010101000000-000000000000
|
||
|
||
// 业务自身的依赖(仅引入当前业务需要的,按需添加)
|
||
require (
|
||
github.com/gin-gonic/gin v1.9.1
|
||
github.com/joho/godotenv v1.5.1 // 环境变量解析
|
||
)
|
||
|
||
// 工作区模式下的本地依赖映射(go work sync 自动生成,无需手动修改)
|
||
replace go-monorepo/common => ../../common
|
||
```
|
||
**关键**:新增/升级业务依赖仅修改当前业务的`go.mod`,其他业务不受影响,实现**发布隔离**。
|
||
|
||
## 4. 业务独立Dockerfile(`services/user/Dockerfile`)
|
||
轻量多阶段构建,仅构建当前业务,镜像体积小,独立构建不影响其他业务,**所有业务的Dockerfile格式统一**(团队规范),示例:
|
||
```dockerfile
|
||
# 构建阶段
|
||
FROM golang:1.21-alpine AS builder
|
||
WORKDIR /app
|
||
# 复制当前业务的代码(仅复制user目录,不涉及其他业务)
|
||
COPY . .
|
||
# 设置Go代理,加速构建
|
||
ENV GOPROXY=https://goproxy.cn,direct
|
||
# 编译(静态编译,无系统依赖)
|
||
RUN go build -ldflags="-s -w" -o user-server main.go
|
||
|
||
# 运行阶段(使用alpine基础镜,体积仅数MB)
|
||
FROM alpine:3.19
|
||
WORKDIR /app
|
||
# 从构建阶段复制编译产物
|
||
COPY --from=builder /app/user-server .
|
||
# 复制业务配置(可选)
|
||
COPY --from=builder /app/conf ./conf
|
||
# 暴露业务端口(user服务示例用8080,order可8081,pay可8082,互不冲突)
|
||
EXPOSE 8080
|
||
# 启动命令
|
||
CMD ["./user-server"]
|
||
```
|
||
**特点**:每个业务单独构建镜像,镜像名区分(如`go-monorepo-user:latest`),发布时仅推送当前业务的镜像即可。
|
||
|
||
## 5. 根目录`docker-compose.yml`(聚合所有业务,支持独立启动)
|
||
核心满足**全局启动所有服务**/**单独启动某一个业务**,所有中间件(PostgresSQL/Redis)全局共享(团队无需为每个业务单独部署中间件),通过`depends_on`控制依赖关系,**业务服务之间通过服务名访问**(如user服务访问order服务:`http://order:8081`)。
|
||
**核心内容**:
|
||
```yaml
|
||
version: '3.8' # 兼容主流Docker版本
|
||
services:
|
||
# 公共中间件:PostgresSQL(所有业务共享,团队统一管理)
|
||
postgres:
|
||
image: postgres:16-alpine
|
||
container_name: monorepo-postgres
|
||
environment:
|
||
POSTGRES_USER: ${PG_USER:-root}
|
||
POSTGRES_PASSWORD: ${PG_PWD:-123456}
|
||
POSTGRES_DB: ${PG_DB:-monorepo}
|
||
ports:
|
||
- "${PG_PORT:-5432}:5432"
|
||
volumes:
|
||
- ./deploy/postgres/init.sql:/docker-entrypoint-initdb.d/init.sql # 初始化脚本
|
||
- pg_data:/var/lib/postgresql/data # 数据持久化
|
||
networks:
|
||
- monorepo-net
|
||
restart: always
|
||
|
||
# 公共中间件:Redis(所有业务共享)
|
||
redis:
|
||
image: redis:7-alpine
|
||
container_name: monorepo-redis
|
||
ports:
|
||
- "${REDIS_PORT:-6379}:6379"
|
||
volumes:
|
||
- ./deploy/redis/redis.conf:/etc/redis/redis.conf
|
||
- redis_data:/data
|
||
command: redis-server /etc/redis/redis.conf
|
||
networks:
|
||
- monorepo-net
|
||
restart: always
|
||
|
||
# 业务服务1:用户服务
|
||
user:
|
||
build:
|
||
context: ./services/user # 仅构建user目录,独立构建
|
||
dockerfile: Dockerfile
|
||
container_name: monorepo-user
|
||
environment:
|
||
- GIN_MODE=release
|
||
- PG_ADDR=postgres:5432 # 访问公共PG,通过服务名
|
||
- REDIS_ADDR=redis:6379 # 访问公共Redis
|
||
ports:
|
||
- "8080:8080"
|
||
volumes:
|
||
- ./logs/user:/app/logs # 日志挂载到本地
|
||
networks:
|
||
- monorepo-net
|
||
restart: always
|
||
depends_on:
|
||
- postgres
|
||
- redis
|
||
# 可选:限制资源(团队开发/测试环境,避免单个服务占满资源)
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
cpus: "0.5"
|
||
memory: "512M"
|
||
|
||
# 业务服务2:订单服务(和user服务结构一致,独立部署)
|
||
order:
|
||
build:
|
||
context: ./services/order
|
||
dockerfile: Dockerfile
|
||
container_name: monorepo-order
|
||
environment:
|
||
- GIN_MODE=release
|
||
- PG_ADDR=postgres:5432
|
||
- REDIS_ADDR=redis:6379
|
||
ports:
|
||
- "8081:8081"
|
||
volumes:
|
||
- ./logs/order:/app/logs
|
||
networks:
|
||
- monorepo-net
|
||
restart: always
|
||
depends_on:
|
||
- postgres
|
||
- redis
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
cpus: "0.5"
|
||
memory: "512M"
|
||
|
||
# 业务服务3:支付服务(独立部署)
|
||
pay:
|
||
build:
|
||
context: ./services/pay
|
||
dockerfile: Dockerfile
|
||
container_name: monorepo-pay
|
||
environment:
|
||
- GIN_MODE=release
|
||
- PG_ADDR=postgres:5432
|
||
- REDIS_ADDR=redis:6379
|
||
ports:
|
||
- "8082:8082"
|
||
volumes:
|
||
- ./logs/pay:/app/logs
|
||
networks:
|
||
- monorepo-net
|
||
restart: always
|
||
depends_on:
|
||
- postgres
|
||
- redis
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
cpus: "0.5"
|
||
memory: "512M"
|
||
|
||
# 全局网络:所有服务在同一个网络,通过服务名互通
|
||
networks:
|
||
monorepo-net:
|
||
driver: bridge
|
||
|
||
# 全局数据卷:中间件数据持久化,不会随容器删除
|
||
volumes:
|
||
pg_data:
|
||
redis_data:
|
||
```
|
||
|
||
## 6. 业务独立`docker-compose.yml`(`services/user/docker-compose.yml`)
|
||
满足**单独部署某一个业务**的场景(如仅修改了user服务,只需发布user,无需启动其他业务),仅包含当前业务+必要的中间件(也可连接外部已有的中间件),示例:
|
||
```yaml
|
||
version: '3.8'
|
||
services:
|
||
user:
|
||
build:
|
||
context: .
|
||
dockerfile: Dockerfile
|
||
container_name: standalone-user
|
||
environment:
|
||
- GIN_MODE=release
|
||
- PG_ADDR=192.168.1.100:5432 # 可连接外部PG/Redis,无需启动本地中间件
|
||
- REDIS_ADDR=192.168.1.100:6379
|
||
ports:
|
||
- "8080:8080"
|
||
volumes:
|
||
- ../../logs/user:/app/logs
|
||
restart: always
|
||
networks:
|
||
default:
|
||
driver: bridge
|
||
```
|
||
|
||
## 7. 全局Makefile(简化团队操作,避免记复杂命令)
|
||
团队统一使用Makefile命令,降低操作成本,**所有命令支持全局/单独业务执行**,示例:
|
||
```makefile
|
||
# 全局启动所有服务(中间件+所有业务)
|
||
up:
|
||
docker compose up -d
|
||
|
||
# 单独启动某一个业务(如user,make up-svc svc=user)
|
||
up-svc:
|
||
docker compose up -d $(svc)
|
||
|
||
# 全局停止所有服务
|
||
down:
|
||
docker compose down
|
||
|
||
# 单独停止某一个业务
|
||
down-svc:
|
||
docker compose stop $(svc)
|
||
|
||
# 构建所有业务镜像
|
||
build:
|
||
docker compose build
|
||
|
||
# 单独构建某一个业务镜像(核心:发布时仅构建当前业务)
|
||
build-svc:
|
||
docker compose build $(svc)
|
||
|
||
# 查看所有服务日志
|
||
logs:
|
||
docker compose logs -f
|
||
|
||
# 查看某一个业务日志
|
||
logs-svc:
|
||
docker compose logs -f $(svc)
|
||
|
||
# 重新启动某一个业务(修改代码后,一键重启)
|
||
restart-svc:
|
||
docker compose restart $(svc)
|
||
|
||
# 清理无用镜像/容器(团队开发环境,定期清理)
|
||
clean:
|
||
docker system prune -f
|
||
|
||
# 初始化Go工作区(新成员拉取代码后,一键执行)
|
||
go-init:
|
||
go work init
|
||
go work use ./common
|
||
go work use ./services/user
|
||
go work use ./services/order
|
||
go work use ./services/pay
|
||
go mod tidy
|
||
```
|
||
**团队使用示例**:
|
||
- 新成员拉取代码:`make go-init`(初始化Go工作区,解决依赖)
|
||
- 开发用户服务:`make build-svc svc=user && make up-svc svc=user`(仅构建+启动user服务)
|
||
- 发布订单服务:`make build-svc svc=order && make restart-svc svc=order`(仅构建+重启order服务,不影响user/pay)
|
||
- 全局启动所有服务(测试环境):`make up`
|
||
|
||
---
|
||
|
||
# 团队开发&发布流程(适配团队)
|
||
## 开发流程(一人负责一个核心业务,互不干扰)
|
||
1. 拉取monorepo代码:`git clone <仓库地址> && cd go-monorepo && make go-init`;
|
||
2. 开发各自业务(如A开发user,B开发order,C开发pay),仅修改`services/[自己的业务]`目录下的代码;
|
||
3. 公共代码修改(如common/logger):需团队沟通,保证兼容性(不修改现有接口,仅新增),避免影响其他业务;
|
||
4. 本地测试:使用`make build-svc svc=xxx && make up-svc svc=xxx`仅启动自己的业务,快速验证。
|
||
|
||
## 发布流程(业务独立发布,不影响其他服务)
|
||
### 生产/测试环境发布单个业务(核心需求)
|
||
```bash
|
||
# 1. 拉取最新代码(仅拉取,不影响运行中的服务)
|
||
git pull origin main
|
||
|
||
# 2. 仅构建当前业务的镜像(如发布pay服务)
|
||
make build-svc svc=pay
|
||
|
||
# 3. 仅重启当前业务容器(秒级重启,不影响其他服务)
|
||
make restart-svc svc=pay
|
||
```
|
||
### 首次部署/全局发布(如测试环境初始化)
|
||
```bash
|
||
make go-init && make build && make up
|
||
```
|
||
|
||
---
|
||
|
||
# 关键满足点验证
|
||
1. ✅ **无需过度封装**:common仅做轻量基础封装,无业务逻辑,业务模块结构简单(api/service/dao/model四层,团队易理解);
|
||
2. ✅ **业务独立部署**:每个业务有独立Dockerfile、compose,支持`make up-svc svc=xxx`单独启动,也可通过业务目录下的compose单独部署;
|
||
3. ✅ **发布互不影响**:发布时仅构建/重启当前业务镜像,其他业务容器正常运行,公共代码变更做兼容性保证;
|
||
4. ✅ **docker compose发布**:根目录compose聚合所有服务,支持全局/单独发布,Makefile封装所有命令,团队使用便捷;
|
||
5. ✅ **团队适配**:一人一个核心业务,目录结构清晰,公共代码统一维护,开发/发布命令简化,无复杂配置。
|
||
|
||
---
|
||
|
||
# 扩展建议(团队可按需添加,不影响现有结构)
|
||
1. **新增业务**:在`services/`下新建业务目录,复制user服务的基础结构(go.mod、Dockerfile、目录分层),在`go.work`和根`docker-compose.yml`中追加一行,即可实现独立部署;
|
||
2. **多环境配置**:在各业务`conf/`下添加`dev/ prod/ test`目录,通过环境变量`ENV=prod`指定配置文件,根`.env`区分不同环境的变量;
|
||
3. **接口文档**:在各业务`api/`下添加`swagger/`目录,使用`swag init`生成接口文档,独立访问(如user服务:`http://localhost:8080/swagger/index.html`);
|
||
4. **监控告警**:根`docker-compose.yml`中新增Prometheus+Grafana服务,所有业务暴露metrics接口,统一监控(轻量配置,团队易维护);
|
||
5. **代码规范**:添加`golangci-lint`配置文件(.golangci.yml),团队提交代码前执行`golangci-lint run`,保证代码风格统一。
|
||
|
||
### 总结
|
||
本次设计的Go Monorepo核心围绕**小团队高效开发**和**业务独立部署发布**,通过Go工作区实现monorepo依赖管理,以`services/`为业务隔离边界,结合Docker和docker compose实现**独立构建、独立发布、全局聚合**,同时通过Makefile封装所有操作,降低团队协作成本。整体结构轻量、可扩展,无需过度设计,完全匹配任务要求。 |