Go单体项目模块化核心是职责边界、显式依赖与接口设计,而非过早物理拆分;应先逻辑分层(domain/application/infrastructure)、包级封装、接口+DI解耦,再按需渐进升级为多module。

先确认是否真需要物理拆分
很多团队一上来就建多个 repo、起多个进程,结果只是把单体服务套了层 HTTP 外壳,数据库还共用,接口耦合没解,反而增加了运维负担。真正该拆的信号只有两个:谁必须和谁一起发布、谁的数据变更不能被别人直接读表。如果当前 main.go 里初始化的模块全共享同一份 DB 连接池、Redis 客户端或配置结构体,那说明它们还在一个部署单元里,暂时不用拆进程——先做逻辑解耦更实际。
用多 cmd/ 目录实现轻量级演进
不急着开新仓库,先在原项目根目录下建 cmd/userapi/main.go 和 cmd/usersvc/main.go 两个入口。它们共用同一份 go.mod,但编译出不同二进制文件。GoLand 会自动识别这些 main 包并允许你单独运行或调试。好处是:代码复用率高、CI 流水线不用重写、本地联调时仍能走本地内存调用(比如用 user.Service 直接调 order.OrderReader 接口),等边界稳定后再抽成独立模块。
GoLand 2026.1.1 是 2026.1 发布后的首个维护修正版本,适合已经开始体验 2026.1 新功能并希望同步补丁的开发者。它更适合用于入门项目、现有项目迁移测试和 IDE 行为验证。
- 确保每个
cmd/xxx/main.go都有独立的init()和依赖注入逻辑,不要在main()里硬编码其他服务地址 - 所有跨域调用必须封装成 interface,例如
type OrderClient interface { GetOrder(ctx context.Context, id string) (*Order, error) } - GoLand 的 “Run Configuration” 支持按
cmd/目录快速切换启动项,右键对应main.go→ “Run” 即可
HTTP 下游调用别写死地址
常见错误是 handler 里直接 http.Get("http://order-svc:8080/v1/order/123")。这会导致本地开发无法联调、生产环境没法加熔断重试、测试时难 mock。GoLand 虽不强制,但能帮你快速识别这类硬编码——打开 “Find Usages” 查 http.Get 或 http.Post,逐个替换为 client 接口调用。
- 下游 client 实现里用
http.Client或grpc.ClientConn,但初始化交给 DI(如dig.In或构造函数) - 生产环境用 Consul 或 DNS SRV 做服务发现,
order-svc:8080这种字符串绝不能出现在代码或 config.json 里 - GoLand 的 “Services Tool Window” 可以手动注册服务实例,方便本地模拟多服务协同
proto 文件别变成新单点故障
用 gRPC 拆服务后,很多人把所有 .proto 放进一个 api/ 仓库,各服务都 go get 它——结果改一个字段就得全链路发版。GoLand 本身不解决这个问题,但能帮你控制影响范围:右键 .proto 文件 → “Generate Code”,勾选 “Only for current module”,避免生成代码污染其他 cmd 目录;同时把 api/ 作为 submodule 引入,而不是直接 go mod tidy 拉取。
- 每个服务只生成自己需要的 stub,不要全量生成
- proto 中的 message 字段加
optional或保留reserved号段,为后续兼容留余地 - GoLand 的 “Protobuf Support” 插件默认启用,但需确认 Settings → Languages & Frameworks → Protocol Buffers 中路径指向正确的
protoc二进制
go mod 不等于一个服务,一个 cmd/ 目录也不等于一个微服务——边界划在哪,得看初始化依赖图和数据所有权,而不是目录名或包名。

















