Kratos 不强制 DDD,但其分层结构天然适配——关键在于是否在 internal 下按领域边界组织代码:biz 放领域模型与接口,data 实现仓储,service 仅作接口实现层并依赖 biz 接口;Protobuf 仅为 DTO,需在 biz 重定义领域模型并设映射层;领域事件与事务应由 biz 定义、data/infra 实现,严禁跨层 import。

Kratos 本身不强制 DDD,但它的分层结构和依赖注入设计天然适配 DDD 落地——关键不是“能不能用”,而是你是否在 internal 目录下真正按领域边界组织代码,而不是把 Kratos 当成一个“带 Wire 的 HTTP 框架”来用。
为什么 Kratos 的目录结构容易误导 DDD 实践
默认生成的 internal 下有 handler、service、data 三层,很多人直接把业务逻辑全塞进 service,再让 handler 调用它,结果变成“HTTP 驱动开发”,领域模型空心化。
-
service层在 Kratos 里本意是“接口实现层”,对应的是api/中定义的 RPC 方法,它不该承载核心领域逻辑 - 真正的领域模型(
Entity、ValueObject、Aggregate)应该放在internal/biz(或显式建internal/domain),且被service层通过接口依赖调用 - 如果没建
biz目录,或者里面只有几个空 struct,那 DDD 就只是目录名而已
如何用 Wire + 接口抽象隔离领域层
Wire 不是只为注入 HTTP/gRPC Server,它是控制依赖流向的关键工具。DDD 要求上层(service)依赖下层(biz)的接口,而非具体实现。
- 在
internal/biz定义领域接口,比如type OrderRepo interface { Save(ctx context.Context, o *Order) error } - 在
internal/data实现该接口,比如mysqlOrderRepo,它只依赖数据库驱动,不 import 任何service或api - 在
internal/service的构造函数中,接收biz.OrderRepo作为参数,而不是自己 new 或 importdata -
wire.go中把mysqlOrderRepo注入到service.NewOrderService,完成依赖反转
Protobuf 定义与领域模型的张力怎么处理
api/ 下的 .proto 文件描述的是通信契约,不是领域模型。硬把 OrderStatus 枚举或 Address 结构直接当领域值对象用,会导致领域逻辑被序列化细节污染。
立即学习“go语言免费学习笔记(深入)”;
- 禁止在
biz层直接使用api.v1.Order—— 它是 DTO,应只出现在handler和网络边界 - 在
biz层重新定义type Order struct { ID string; Status OrderStatus },其中OrderStatus是自定义枚举,含业务方法如CanCancel() bool - 写一个轻量映射层(比如
biz.ToDomainOrder(apiOrder *v1.Order) *biz.Order),放在internal/service或internal/transfer,不放biz - Protobuf 的
validate规则只能做基础校验(非空、长度),复杂业务规则(如“支付超时订单不可改地址”)必须在biz层抛err
容易被忽略的 DDD 关键点:领域事件与仓储生命周期
Kratos 默认不提供领域事件总线或仓储事务钩子,这些得自己补,但不能破坏分层。
- 领域事件(
biz.OrderCreatedEvent)应在biz层 new 并由聚合根Apply()发出,但发布动作(如发 Kafka)属于基础设施,由data或单独infra层实现 - 仓储接口(
OrderRepo)应支持事务上下文,比如Save(ctx context.Context, o *Order) error中的ctx可携带tx,具体实现由data层判断是否启用 DB transaction - 别在
service层手动开事务——那是data层的职责;也别让biz层 importdatabase/sql
DDD 在 Kratos 里不是靠框架功能自动生效的,它靠的是你在 internal 里每建一个目录、每写一个 interface、每删一次跨层 import 时的克制。一旦开始把 service 当万能胶,DDD 就结束了。



















