go模块不提供微服务框架能力,需通过go mod划清服务边界、接口定义前置、http/grpc通信层隔离来实现轻量级契约:每个服务独立module path,domain放internal不暴露;transport层只处理请求响应,业务逻辑下沉service;grpc用.proto驱动生成代码;健康检查与配置加载需解耦main.go。

Go 模块本身不提供微服务框架能力,所谓“轻量级契约”必须靠显式设计落地——核心是用 go mod 管理依赖边界 + 接口定义前置 + HTTP/gRPC 通信层隔离。
用 go mod init 划清服务边界,别让 domain 包被外部直接 import
很多人一上来就 go mod init mysvc,然后把 handler、service、domain 全塞进同一个 module。结果是其他服务能直接 import mysvc/domain,契约瞬间失效。
- 每个微服务应有独立 module path,比如
go mod init github.com/org/auth-svc - 只导出
cmd/和internal/下的接口,domain/和model/放在internal/里,不暴露给 go.mod 的 require 列表 - 若需共享类型(如通用 error 或 DTO),单独建
github.com/org/sharedmodule,并用replace在本地开发时指向本地路径,避免循环依赖
HTTP 路由层只处理 transport,别在 http.HandlerFunc 里写业务逻辑
常见错误:把数据库查询、校验、事件发布全堆在 http.HandleFunc 里,导致无法复用、难以测试、契约模糊。
- 定义清晰的 transport 层:用
http.ServeMux或轻量库(如chi)注册路由,只做请求解析、响应封装 - 业务逻辑全部下沉到
internal/service/,该层接收纯 Go struct(非*http.Request),返回明确 error 类型(如service.ErrNotFound) - 错误映射统一在 transport 层完成:比如将
service.ErrInvalidInput转为400 Bad Request,避免 service 层感知 HTTP 状态码
gRPC 接口定义用 .proto 文件驱动,生成代码别手写
“契约”不是靠文档或注释维持的,而是靠 .proto 文件强制约束。手写 gRPC server 实现或直接用 struct 传参,等于放弃契约。
-
api/v1/auth.proto必须放在独立目录(如api/),并提交到 Git —— 这才是真正的服务契约 - 用
protoc --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:.生成代码,生成的AuthClient和AuthServiceServer接口就是调用方与实现方的唯一约定 - 不要在
.proto里定义复杂嵌套或任意字段(google.protobuf.Struct),这会让客户端无法静态校验;宁可多定义几个小 message
健康检查和配置加载必须脱离 main.go,用 init() 或构造函数封装
很多脚手架把 database.Open()、redis.NewClient()、http.ListenAndServe() 全塞进 main(),导致无法单元测试、无法替换依赖、无法做依赖注入。
- 把启动逻辑拆成
cmd/auth-svc/main.go(只负责 flag 解析 + 构造App实例 +app.Run()) -
internal/app/app.go定义type App struct,其NewApp()构造函数接收所有依赖(DB、Cache、Logger),便于测试时 mock - 健康检查端点(如
/healthz)应调用app.healthChecker.Check(),而不是直接 ping 数据库连接 —— 检查逻辑本身也要可替换、可超时控制
真正难的不是写多少行代码,而是每次加新功能时,能否一眼看出该放 internal/transport 还是 internal/service;契约失效往往发生在第 3 次迭代时,有人悄悄绕过 interface 直接 new 了一个 repository 实例——这时候 go mod graph 和 go list -f '{{.Deps}}' ./... 就是你唯一的审计工具。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











