微服务项目必须拆出 cmd/ 子目录:每个服务独占 cmd/ 下同名子目录,main.go 仅加载配置、调用 bootstrap.init()、监听信号;internal/ 按服务边界切分,禁止跨服务 import;pkg/ 仅放被多服务依赖、有明确 contract 和版本的可复用模块;配置、api、部署脚本须按服务隔离。

微服务项目必须拆出 cmd/ 子目录,不能只放一个 main.go
单个 main.go 在微服务场景下会迅速失控:多个服务(如 auth、order、notification)共用同一入口,导致构建产物混淆、配置无法隔离、CI/CD 难以按服务粒度发布。
正确做法是每个服务独占 cmd/ 下的子目录,且目录名与生成的二进制名一致:
-
cmd/auth/main.go→ 构建出auth可执行文件 -
cmd/order/main.go→ 构建出order可执行文件 -
main.go里只做三件事:加载配置、调用bootstrap.Init()、监听信号退出
这样既能用 go build -o ./bin/auth ./cmd/auth 精确构建单个服务,也能在 CI 中对不同服务设置独立的测试和镜像构建流程。
internal/ 要按“服务边界”而非“技术分层”切分
常见错误是把所有服务塞进统一的 internal/handler、internal/service,结果 auth 的 handler 依赖了 order 的 service,造成隐式耦合,一改全崩。
应为每个微服务在 internal/ 下设独立根包:
-
internal/auth/:含handler/、service/、repository/、model/ -
internal/order/:同上,完全隔离 - 跨服务通信只能通过 API 或消息队列,禁止包级 import
Go 编译器会阻止外部模块导入 internal/ 下代码,但不阻止本项目内跨服务 import —— 这个保护机制只靠目录结构没用,必须靠人盯住 import 路径。
pkg/ 不是“工具函数集合区”,而是可复用库的发布接口
把日志封装、HTTP client 封装、JWT 解析都扔进 pkg/utils 是典型反模式。这些代码若没经过抽象、没写文档、没考虑外部使用场景,就不该放 pkg/。
真正该进 pkg/ 的只有:
- 被至少两个以上微服务直接 import 的组件(如统一 trace 上报
pkg/tracing) - 对外提供明确 contract 的 SDK(如
pkg/payment封装支付网关,含 mock 实现和 test helper) - 经单元测试覆盖、有版本 tag、能独立
go get的模块
否则一律放 internal/common 或直接复制到各服务下 —— 微服务的第一性原理是“独立演进”,过早抽象共享库反而拖慢迭代。
配置、API 定义、部署脚本必须和服务一一对应
很多人把 configs/app.yaml 放项目根目录,结果 auth 和 order 共用同一份配置,环境变量覆盖逻辑混乱,K8s ConfigMap 模板难维护。
实际要按服务组织:
-
configs/auth/下放dev.yaml、prod.yaml、schema.json -
api/auth/v1/放 OpenAPI spec 和生成的 Go client(用go:generate自动更新) -
deployments/auth/k8s/deployment.yaml专用于 auth 服务,镜像名、资源限制、探针路径全绑定
最容易被忽略的是:不同服务的配置结构体定义(如 type AuthConfig struct)必须放在各自 internal/auth/config.go 里,而不是统一塞进 pkg/config —— 配置是服务契约的一部分,不该被强耦合。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











