diff --git a/README.md b/README.md index e90669d..7ffd94a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,406 @@ -# trading_assistant +# 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封装所有操作,降低团队协作成本。整体结构轻量、可扩展,无需过度设计,完全匹配任务要求。 \ No newline at end of file