重构前必须写测试,否则等于在雷区裸奔;接口定义须置于Domain或UseCase层,禁止Infrastructure声明;包依赖须严格遵循整洁架构,usecase不得import数据库包;函数拆分应按数据流而非功能块,错误处理需统一收口。

重构前必须写测试,否则等于在雷区裸奔
没有测试覆盖的重构,本质是拿业务稳定性赌运气。Go项目里常见的情况是:改完UserService的某个方法,结果OrderService调用它时panic了,但没人发现——因为没测。
- 先补关键路径的单元测试,重点覆盖
UseCase层的输入输出边界 - 用
testify/mock或gomock打桩依赖,确保测试不走真实DB或HTTP - 测试文件命名保持
xxx_test.go,函数名以Test开头,比如TestCreateUser - 运行
go test -cover确认覆盖率,核心逻辑建议不低于70%
接口定义必须放在Domain或UseCase层,不能在Infrastructure里声明
很多团队把UserRepository接口写在internal/infrastructure/mysql/下,这直接违反整洁架构的依赖规则——外层实现不能决定内层契约。
- 正确位置是
internal/domain/或internal/usecase/,比如internal/domain/user_repository.go - 接口方法只暴露业务语义,不带技术细节:
Save(ctx context.Context, u *User) error,而不是SaveToMySQL - 实现类才放在
internal/infrastructure/,且必须实现内层定义的接口,不能反向依赖 - 编译期就能发现错误:如果实现类引用了
domain层没导出的类型,go build会直接报错
包名和目录结构不是风格问题,而是依赖流向的物理体现
Go里import路径就是硬性依赖关系。把delivery(HTTP handler)包放进internal/usecase目录下,等于默许它直接调用数据库代码。
-
internal/domain/:只放struct、interface、核心校验逻辑,不import任何外部包 -
internal/usecase/:调用domain,定义业务流程,可importdomain和errors,但不能importsql或gin -
internal/delivery/和internal/infrastructure/:只作为适配器,负责把HTTP请求转成UseCase能理解的参数,再把结果转回JSON - 一旦发现
usecase包import了mysql或redis,说明分层已经塌陷,得立刻修复
函数拆分要按“数据流”而非“功能块”,否则容易割裂业务语义
看到ProcessOrder太长就机械地按if/else切分成validateOrder、notifyUser、updateStock,结果每个函数都要传一堆参数,反而更难理解整体意图。
立即学习“go语言免费学习笔记(深入)”;
- 优先提取的是“数据转换节点”:比如从
http.Request到usecase.OrderInput的映射逻辑,单独成函数 - 保持业务动词完整:一个UseCase函数应该表达一个明确动作,如
CreateArticle,而不是doStep1ThenStep2 - 参数过多时用结构体封装:
type CreateUserInput struct { Name string; Email string },比5个string参数更清晰 - 错误处理统一收口:不要每个子函数都
log.Fatal,UseCase层集中做errors.Is判断和分类返回
init()函数悄悄注册了全局DB连接,或者log包被直接import进Domain层。这些点不会报错,但会让分层失效,必须逐行检查import链。


















