# 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封装所有操作,降低团队协作成本。整体结构轻量、可扩展,无需过度设计,完全匹配任务要求。