判断go模块能否拆为独立服务,关键看其是否具备独立生命周期(db.open/close自主控制)、连接池不复用、配置可单独覆盖、.proto契约由服务方定义且版本化、下游调用封装为client接口、禁止直连其他服务数据库、internal包不被外部导入。

怎么判断一个 Go 模块该不该拆成独立服务
不能只看包名里有没有 user 或 order——那只是目录名,不是服务。真正要验证的是它能不能独立启停、配 DB、不碰别的模块的 internal。
先跑这句命令画出初始化依赖图:go list -f '{{.Deps}}' ./cmd/main。如果输出里显示 user 强依赖 order.Repository,或者两者共享同一个 *sql.DB 实例,那它现在只是个模块,不是服务。
- 能拆的前提:有自己完整的生命周期(
DB.Open和DB.Close在本服务内控制) - 连接池不复用(不能共用
sql.DB或redis.Client) - 配置可单独覆盖(比如
config/user.yaml不被其他服务读)
proto 契约没写对,比数据库连不上还难排查
错误现象不是编译失败,而是运行时 panic: proto: cannot parse invalid wire-format data——这通常是因为字段变更不兼容,旧客户端反序列化失败,但日志里根本看不出是哪个字段、哪个服务惹的祸。
.proto 不是通信协议,是契约。契约崩了,整个链路就断了。
在 Go 中使用 google/wire 实现编译时依赖注入——wire.NewSet、wire.Build、wire.Bind(接口→实现)、wire.Struct、wire.Value、wire.Interface
-
.proto必须由服务提供方定义,放在统一路径如api/user/v1/user.proto,禁止散落在各服务的internal/下 - 禁止定义聚合接口,比如
rpc GetUserWithOrders()——这等于把组合逻辑强绑在契约上,下游一加字段你就得改.proto、发版、等所有客户端升级 - 版本必须写进包名,如
package userpbv2;,而不是靠 URL 路径(/api/v2/user对 gRPC 无效) - 字段变更只允许追加、重命名(用
reserved)、或设为optional;删除或改类型会直接导致旧 client 解析失败
HTTP handler 里硬编码下游地址,等于放弃弹性
常见错误现象:http.HandleFunc("/pay", func(w http.ResponseWriter, r *http.Request) { resp, _ := http.Get("http://order-svc:8080/v1/order/123") })——这种写法会让服务失去弹性,无法做重试、超时、熔断,且本地开发时根本没法联调。
- 所有下游调用必须封装成
client接口,例如type OrderClient interface { GetOrder(ctx context.Context, id string) (*Order, error) } - 实现里才用
http.Client或 gRPC stub;client 初始化交给 DI 容器(如dig或手写构造函数),不要在 handler 里new - 生产环境用服务发现(如 Consul +
go-micro/registry/consul)或 DNS SRV 记录,避免把order-svc:8080写死在代码或config.json里
模块间直连数据库或 DAO,等于把耦合写死在编译期
禁止在 order-service 里直接 import user-service/internal/repository 或拼 SQL 查 user 表——这等于把耦合写死在编译期。
数据自治不是口号:每个服务的 go.mod 里不该出现其他服务的数据库驱动依赖;repository 层只对接本服务私有 DB,对外只暴露 GetUserByID(ctx, id) 这类能力接口。
- 跨包调用必须先抽象接口,比如
order.OrderReader,让user只依赖接口,再谈物理隔离 - 初期可用多
main入口(cmd/userapi/main.go和cmd/usersvc/main.go)共用同一仓库,但二进制分离部署——这是最轻量的演进起点 - 检查所有
internal包是否被外部服务 import;Go 的internal机制是编译期防护,但若路径泄露或误导包,照样破防
.proto 文件里混着用户资料、认证令牌、权限策略,改一个字段就得全链路发版;又比如两个服务共用一套 config struct,其中一个加了字段,另一个启动失败却报错在 yaml.Unmarshal,根本看不出源头在哪。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










