go领域驱动设计强调domain包承载业务语义、application层仅编排不决策、infrastructure层屏蔽技术细节、测试驱动边界清晰,通过聚合根、值对象、领域事件等实现高内聚低耦合。

Go 语言本身没有强制的领域建模范式,但随着业务复杂度上升,把业务逻辑硬塞进 handler、service 或 dao 层会导致代码难以理解、测试困难、变更成本高。解耦的关键不是堆砌分层,而是明确边界、约束流向、让业务规则可识别、可组合、可验证。
用 domain 包承载真实业务语义
domain 目录下不放结构体定义和空接口,而是聚焦“谁在什么条件下做了什么事”。比如订单场景中,Order 是聚合根,它内聚状态(Status、Items)、行为(Confirm()、Cancel())和不变量校验(如“已发货不能取消”)。所有状态变更必须通过方法触发,而非直接改字段。外部调用方只看到 Order.Confirm(),看不到数据库字段或事务细节。
- 聚合根负责一致性边界:一个 Order 的创建、支付、发货应在一个事务内完成,但跨 Order 的对账可异步处理
- 值对象表达无身份概念:Money、Address、SKUCode 应不可变,带校验逻辑(如 Money.New(100, "CN Y") 拒绝负金额)
- 领域事件显式建模业务事实:OrderPaid、InventoryDeducted 等事件由聚合内部发布,不依赖消息中间件实现
application 层只编排,不决策
application 包是用例入口,职责清晰:接收输入(DTO)、协调 domain 对象、触发基础设施操作(发消息、查缓存、写 DB),但不包含 if-else 业务分支。例如 “下单” 用例里,application 层拿到 CreateOrderRequest 后,仅做参数转换、调用 orderService.Create()、publish(OrderCreated),而“是否允许下单”“库存是否充足”等判断全部下沉到 domain 层的 Order 或 Inventory 领域服务中。
- 避免在 application 层做领域规则判断,否则规则散落、重复、难追溯
- DTO 和 domain 对象严格分离:request → application → domain → persistence,禁止 domain 结构体直接绑定 HTTP 参数
- 用命令/查询分离(CQRS)简化读写路径:QueryHandler 只查,CommandHandler 只改,不混用
infrastructure 层屏蔽技术细节,暴露契约
数据库、Redis、HTTP 客户端等都属于 infrastructure。关键不是“把 ORM 封装一下”,而是为 domain 提供抽象接口,让业务代码不感知底层实现。例如定义 interface Repository { Save(Order) error; FindByID(ID) (*Order, error) },具体 MySQLRepo 或 MemoryRepo 都实现它。domain 层只依赖 interface,测试时可注入内存实现,上线时切换为 MySQL 实现,零修改。
立即学习“go语言免费学习笔记(深入)”;
- 领域事件发布也走接口:EventPublisher.Publish(event) → KafkaPublisher 或 NatsPublisher 实现
- 第三方调用封装为 domain 可理解的概念:PaymentGateway.Charge() 返回 PaymentResult,而非 raw HTTP response
- 避免 infrastructure 包反向引用 application 或 domain:依赖只能单向从上往下(domain ← application ← infrastructure)
测试驱动边界清晰度
真正检验解耦效果的是测试。domain 层单元测试应完全脱离数据库、网络、时间等外部依赖,只验证行为逻辑。比如测试 Order.Cancel() 是否在 Status == Paid 时返回错误,只需构造 Order 实例并调用方法。application 层测试则用 mock 替换 infrastructure 接口,验证流程是否按预期编排。一旦发现某个测试必须启动 MySQL 或等待 HTTP 响应,就说明边界被穿透了。
- domain 测试用纯 Go 写,不引入 testify/gomega 等框架也可,重点是断言业务结果
- application 测试用 gomock 或 testify/mock,验证是否调用了正确的 repository 方法、是否发布了正确事件
- 集成测试放在单独目录(如 integration/),覆盖跨层链路,但不替代单元测试










