第一章:Go项目DDD落地困境的根源诊断与破局共识
Go语言简洁、高效、面向工程实践,但其原生缺乏类继承、泛型(在1.18前)、领域建模抽象设施等特性,导致团队在尝试DDD落地时频繁遭遇“理念正确、代码失焦”的断层。常见症结并非概念理解偏差,而是技术选型、分层契约与团队认知三者未对齐。
领域模型与基础设施的耦合陷阱
许多项目将数据库结构直接映射为Entity,用gorm.Model嵌入ID/CreatedAt字段,再在Service层拼装业务逻辑——这实质是CRUD思维披着DDD外衣。正确做法是严格隔离:Domain层仅定义接口(如UserRepository),Infrastructure层实现具体存储(如postgresUserRepo),并通过依赖注入传递。示例:
// domain/user.go —— 无import db相关包,不暴露SQL细节
type User struct {
ID UserID
Name string
}
func (u *User) ChangeName(newName string) error {
if newName == "" {
return errors.New("name cannot be empty")
}
u.Name = newName
return nil
}
分层边界模糊引发的职责漂移
以下行为会破坏限界上下文完整性:
- 在Application层直接调用
http.Get()发起外部API请求 - 在Domain层使用
time.Now()而非依赖Clock接口 - Entity方法中执行日志记录或发送消息
应统一通过接口抽象外部依赖,并在Application层完成组装:
| 层级 | 允许依赖的层 | 禁止行为 |
|---|---|---|
| Domain | 无(仅标准库) | 调用任何外部I/O或框架类型 |
| Application | Domain + Interfaces | 直接实例化具体infra实现 |
| Infrastructure | Application接口 | 向上层暴露SQL/HTTP细节 |
团队协作中的隐性共识缺失
DDD不是编码规范,而是协作协议。若Product Owner无法用统一语言描述“订单已支付”与“订单已确认”的状态差异,开发便只能靠注释猜测意图。建议启动DDD工作坊时,强制产出三样交付物:
- 限界上下文映射图(含上下文间关系:共享内核/客户-供应商/遵奉者)
- 核心域术语表(如“履约单”≠“发货单”,明确定义与生命周期)
- 每个聚合根的状态迁移图(用Mermaid语法绘制,确保开发与产品共读)
破局起点不在工具链升级,而在每日站会中坚持用统一语言提问:“这个函数变更的是哪个聚合的状态?它会影响哪些上下文?”
第二章:领域分层失焦的纠偏实践
2.1 领域驱动四层架构在Go中的语义对齐:package组织、接口契约与依赖流向
Go 语言无内置分层语法,但可通过 package 边界、接口定义与导入关系实现严格的 DDD 四层语义对齐。
package 组织即架构宣言
domain/:仅含实体、值对象、领域服务接口,零外部依赖application/:实现用例编排,依赖domain接口,禁止引用 infrainfrastructure/:提供domain接口的具体实现(如 MySQLRepo、HTTPClient)interfaces/:HTTP/gRPC 入口,仅依赖application,单向指向核心
接口契约的 Go 实践
// domain/user.go
type UserRepository interface {
Save(ctx context.Context, u *User) error
FindByID(ctx context.Context, id UserID) (*User, error)
}
UserRepository声明在domain/包中,定义能力而非实现;ctx context.Context显式传递生命周期控制权,避免隐式依赖。
依赖流向约束(mermaid)
graph TD
A[interfaces] -->|依赖| B[application]
B -->|依赖| C[domain]
D[infrastructure] -->|实现| C
style A fill:#e6f7ff,stroke:#1890ff
style C fill:#fff7e6,stroke:#faad14
| 层级 | 可导入包 | 禁止导入包 |
|---|---|---|
domain |
无(仅标准库) | application, infra |
application |
domain |
infrastructure |
2.2 基于go:generate与embed的领域模型代码自动生成与验证机制
核心设计思想
将领域模型定义(如 model.yaml)嵌入二进制,并在构建时触发 go:generate 自动生成类型安全的 Go 结构体与校验器。
自动生成流程
//go:generate go run gen/modelgen.go -schema=embed://models/model.yaml
package main
import _ "embed"
//go:embed models/model.yaml
var modelSchema []byte
逻辑分析:
go:embed将 YAML 模型定义编译进包,go:generate调用自定义生成器读取嵌入内容;-schema=embed://...是自定义协议,由生成器解析为io.Reader。参数-schema指定源路径,支持file://和embed://双模式。
验证能力对比
| 特性 | 手动维护 | embed + generate |
|---|---|---|
| 类型安全性 | 易出错 | ✅ 编译期保障 |
| Schema 变更响应延迟 | 小时级 | 秒级(make gen) |
graph TD
A[model.yaml] -->|embed| B(Go binary)
A -->|go:generate| C[gen/modelgen.go]
C --> D[domain/User.go]
C --> E[validator/User_validate.go]
2.3 应用层与领域层边界治理:CQRS拆分+Handler/Command/DomainEvent命名空间隔离
清晰的分层边界是避免贫血模型与职责混淆的关键。CQRS天然支持读写分离,而命名空间隔离则从编译期强化语义契约。
命名空间结构示意
// Application/Commands/CreateOrderCommand.cs
public record CreateOrderCommand(Guid CustomerId, List<OrderItem> Items);
// Application/Handlers/CreateOrderCommandHandler.cs
public class CreateOrderCommandHandler : ICommandHandler<CreateOrderCommand>
{
private readonly IOrderRepository _repo;
public CreateOrderCommandHandler(IOrderRepository repo) => _repo = repo;
public async Task Handle(CreateOrderCommand cmd, CancellationToken ct)
{
var order = Order.Create(cmd.CustomerId, cmd.Items); // 领域逻辑封装
await _repo.SaveAsync(order, ct);
// → 触发领域事件:order.Created()
}
}
ICommandHandler<T> 约束仅在 Application 层实现;Order.Create() 是 Domain 层纯业务构造,不依赖基础设施。
事件发布与响应解耦
| 事件类型 | 发布位置 | 订阅者归属 |
|---|---|---|
OrderCreated |
Domain 层实体 | Application 层 EventHandler |
InventoryReserved |
Integration 层 | External Service |
数据同步机制
// Domain/Events/OrderCreated.cs
public record OrderCreated(Guid OrderId, DateTime OccurredAt) : IDomainEvent;
该类型仅含状态快照与时间戳,无行为,确保序列化安全与跨层兼容性。
graph TD
A[Application.Command] -->|调用| B[Domain.Aggregate]
B -->|发布| C[DomainEvent]
C --> D[Application.EventHandler]
D --> E[SendNotification / UpdateReadModel]
2.4 领域服务与应用服务的职责判定矩阵:基于Go interface内聚性与调用链路分析
判定核心维度
领域服务应仅封装跨实体/值对象的领域逻辑,且不依赖基础设施;应用服务负责用例编排、事务边界与DTO转换。
内聚性验证示例
// ✅ 高内聚:纯领域行为,无外部依赖
type PricingCalculator interface {
CalculateFinalPrice(item Product, coupon *Coupon) (Money, error)
}
// ❌ 低内聚:混入仓储与通知,违反单一职责
type OrderProcessor interface {
Process(order Order) error // 含DB.Save() + email.Send()
}
PricingCalculator 仅操作领域对象,参数 Product 和 Coupon 均为领域模型,返回 Money(值对象),符合领域服务契约;OrderProcessor 若实际调用 repository.Save() 或 notifier.Send(),则已越界至应用层。
职责判定矩阵
| 维度 | 领域服务 | 应用服务 |
|---|---|---|
| 输入参数 | 全为领域对象 | 含DTO、ID、原始类型 |
| 输出结果 | 领域对象或值对象 | DTO、error、状态码 |
| 调用链深度 | ≤1层领域对象间协作 | 可串联领域服务+仓储+外部API |
调用链路可视化
graph TD
A[HTTP Handler] --> B[CreateOrderAppService]
B --> C[OrderValidator.Validate]
B --> D[InventoryService.Reserve]
B --> E[PaymentGateway.Charge]
C & D --> F[Domain Layer]
E --> G[Infrastructure Layer]
2.5 Go泛型驱动的通用领域基类设计:Entity、AggregateRoot、ValueObject的零反射实现
Go 1.18+ 泛型使领域模型基类摆脱 interface{} 和 reflect 成为可能。核心在于类型约束与零成本抽象。
类型契约定义
type Identifier interface {
string | int64 | uuid.UUID // 支持常见ID类型
}
type Entity[ID Identifier] struct {
ID ID
Version uint
CreatedAt time.Time
}
Entity[ID Identifier]将ID类型静态绑定,编译期校验;Version和CreatedAt作为所有实体共性字段,无需运行时类型断言。
基类能力对比表
| 基类 | 是否可比较 | 是否可序列化 | 是否需实现 Equal() |
|---|---|---|---|
Entity[ID] |
否(含时间戳) | 是(结构体) | 否(字段级自动) |
ValueObject[T] |
是(T可比较) | 是 | 否(直接 ==) |
AggregateRoot[ID] |
否 | 是 | 是(需管理领域事件) |
领域对象生命周期
graph TD
A[NewEntity] --> B[ApplyEvent]
B --> C[GetChanges]
C --> D[ClearEvents]
D --> E[Snapshot]
第三章:仓储实现错位的重构路径
3.1 仓储接口抽象陷阱识别:Go中interface过度泛化与SQL/NoSQL混合访问的矛盾
当为 MySQL 和 MongoDB 共同设计 Repository 接口时,常见反模式是定义泛化方法如:
type Repository interface {
Save(ctx context.Context, entity interface{}) error
FindByID(ctx context.Context, id string) (interface{}, error)
List(ctx context.Context, filter map[string]interface{}) ([]interface{}, error)
}
该接口隐含严重问题:
Save()无法表达事务边界(SQL)与原子操作(MongoDB 的Upsert)语义差异;FindByID()对 SQL 需主键类型(int64),而 MongoDB 通常用ObjectID,强制string转换丢失类型安全;filter参数模糊,SQL 依赖预编译参数绑定,MongoDB 则直接接受 BSON 文档。
| 维度 | SQL 场景 | NoSQL(MongoDB)场景 |
|---|---|---|
| 主键类型 | int64, uuid.UUID |
primitive.ObjectID |
| 查询构造 | sqlx.NamedQuery |
bson.M{"status": "active"} |
| 错误语义 | sql.ErrNoRows |
mongo.ErrNoDocuments |
数据同步机制
需解耦读写路径:SQL 侧保留 UserRepo 结构体实现 UserRepository 接口,MongoDB 侧另建 EventLogStore —— 同一领域实体,按存储语义分治。
3.2 Repository + Unit of Work双模式适配:基于context.Context与sync.Pool的事务生命周期管理
核心设计动机
传统 ORM 常将事务绑定到 DB 连接,导致跨 Repository 调用时难以统一回滚。本方案通过 context.Context 透传事务上下文,配合 sync.Pool 复用 Unit of Work 实例,实现轻量、无侵入的生命周期托管。
生命周期协同机制
type UnitOfWork struct {
tx *sql.Tx
ctx context.Context
repo map[string]Repository
}
var uowPool = sync.Pool{
New: func() interface{} {
return &UnitOfWork{repo: make(map[string]Repository)}
},
}
// 从 context 提取或新建 UoW
func GetUnitOfWork(ctx context.Context) *UnitOfWork {
if uow, ok := ctx.Value(uowKey).(*UnitOfWork); ok {
return uow
}
uow := uowPool.Get().(*UnitOfWork)
uow.ctx = ctx
return uow
}
逻辑分析:
sync.Pool避免高频分配;ctx.Value()实现跨层传递;uowKey为私有struct{}类型键,确保类型安全。GetUnitOfWork是无副作用纯函数,支持并发安全复用。
模式适配对比
| 维度 | Repository 模式 | Unit of Work 模式 |
|---|---|---|
| 事务控制 | 各自开启/提交 | 全局统一提交或回滚 |
| 上下文依赖 | 无 | 强依赖 context.Context |
| 对象复用率 | 低(每次 new) | 高(sync.Pool 回收) |
graph TD
A[HTTP Handler] -->|ctx.WithValue| B[Service Layer]
B --> C[GetUnitOfWork]
C --> D{UoW in ctx?}
D -->|Yes| E[复用已有实例]
D -->|No| F[从 Pool 获取+初始化]
E & F --> G[执行多个 Repository 操作]
G --> H[Commit/rollback via ctx]
3.3 DDD仓储在Go中的轻量级实现范式:内存仓储→SQLite测试仓储→PostgreSQL生产仓储的渐进演进
DDD仓储接口统一抽象数据访问,Go中通过依赖倒置实现多环境适配:
type UserRepository interface {
Save(ctx context.Context, u *User) error
FindByID(ctx context.Context, id string) (*User, error)
}
该接口不绑定具体存储,为三类实现提供契约基础。Save 和 FindByID 是核心操作,ctx 支持超时与取消,*User 为领域实体指针,确保状态一致性。
实现演进路径
- 内存仓储:用于单元测试,零外部依赖,响应快但非持久化
- SQLite仓储:嵌入式、文件级事务,适合集成测试与CI流水线
- PostgreSQL仓储:支持并发、索引、外键,满足生产高可用要求
存储选型对比
| 特性 | 内存仓储 | SQLite仓储 | PostgreSQL仓储 |
|---|---|---|---|
| 启动开销 | 极低 | 低(单文件) | 中(需服务进程) |
| 并发写入安全 | ❌(需手动加锁) | ⚠️(WAL模式可) | ✅ |
| 查询能力 | 线性遍历 | SQL92 + 索引 | 全功能SQL + JSONB |
graph TD
A[UserRepository 接口] --> B[MemoryUserRepo]
A --> C[SQLiteUserRepo]
A --> D[PGUserRepo]
B -.->|UT: 快速验证逻辑| E[单元测试]
C -->|IT: 模拟真实SQL行为| F[集成测试]
D -->|RT: 分布式事务/连接池| G[生产部署]
第四章:事件驱动断裂的缝合方案
4.1 领域事件建模规范:Go struct tag驱动的版本兼容性、序列化策略与上下文传播
领域事件需在演化中保持向后兼容,Go 的 struct tag 是声明式契约的核心载体。
标准化 tag 设计
type OrderCreated struct {
ID string `json:"id" avro:"id" v:"1"` // 业务主键,v=1 表示首次引入该字段
UserID string `json:"user_id" avro:"user_id"` // 兼容旧版 JSON key,avro schema 中保持一致
Amount float64 `json:"amount" avro:"amount_cents" v:"2"` // v:"2" 表示字段语义升级(单位从元→分)
CreatedAt time.Time `json:"created_at" avro:"created_at" ctx:"trace_id,span_id"` // 自动注入链路追踪上下文
}
v tag 控制字段生命周期,服务可依据 v 值跳过未知版本字段反序列化;ctx tag 声明需透传的分布式追踪字段,由事件总线自动注入/提取。
序列化策略映射表
| Tag Key | 用途 | 示例值 | 运行时行为 |
|---|---|---|---|
json |
JSON 序列化字段名 | "order_id" |
encoding/json 使用此别名 |
avro |
Avro Schema 字段名 | "order_id_v2" |
生成 .avsc 时覆盖默认命名 |
ctx |
上下文传播字段白名单 | "trace_id,span_id" |
拦截器自动注入/剥离 HTTP header |
数据同步机制
graph TD
A[Producer] -->|Inject ctx via tag| B[Event Bus]
B --> C{Deserializer}
C -->|v≥2: parse amount_cents| D[Handler v2]
C -->|v=1: fallback to amount| E[Handler v1]
4.2 事件总线解耦实现:基于channel+select的内存总线与基于Redis Stream的分布式总线统一抽象
统一事件总线接口设计
定义 EventBus 接口,屏蔽底层差异:
type EventBus interface {
Publish(ctx context.Context, topic string, event interface{}) error
Subscribe(ctx context.Context, topic string) (<-chan Event, error)
Close() error
}
Publish序列化事件并路由至对应后端;Subscribe返回阻塞式只读通道,适配 channel(内存)与 Redis Stream(分布式)两种消费语义。event为任意可序列化结构,topic作为逻辑分区键。
双后端实现对比
| 特性 | 内存总线(channel+select) | Redis Stream 总线 |
|---|---|---|
| 延迟 | 微秒级 | 毫秒级(网络+序列化开销) |
| 持久性 | 无(进程重启丢失) | 有(支持ACK与重播) |
| 并发消费者支持 | 单 goroutine 模拟(需额外分发) | 原生多消费者组(consumer group) |
数据同步机制
内存总线通过 select 非阻塞轮询多个 chan Event;Redis 总线使用 XREADGROUP + BLOCK 实现长轮询。
// Redis 订阅核心逻辑(简化)
func (r *redisBus) Subscribe(ctx context.Context, topic string) (<-chan Event, error) {
ch := make(chan Event, 1024)
go func() {
defer close(ch)
for {
// XREADGROUP GROUP g1 c1 STREAMS topic > COUNT 1 BLOCK 5000
msgs, err := r.client.XReadGroup(ctx, &redis.XReadGroupArgs{
Group: "g1",
Consumer: "c1",
Streams: []string{topic, ">"},
Count: 1,
Block: 5000,
}).Result()
if err != nil { continue }
for _, msg := range msgs[0].Messages {
ch <- decodeEvent(msg.Values)
}
}
}()
return ch, nil
}
">"表示读取最新未处理消息;Block 5000避免空轮询;decodeEvent将map[string]string反序列化为结构体,兼容 JSON/Binary 编码。
4.3 事件溯源(ES)与CQRS协同:Go中Aggregate状态重建的快照+事件流双路径加载策略
在高吞吐场景下,纯事件回放易导致重建延迟。采用快照+增量事件流双路径加载可显著优化性能。
状态重建策略选择逻辑
- 若存在有效快照且其版本 ≥ 最近事件版本 → 直接加载快照,再重放后续事件
- 否则执行全量事件回放
- 快照定期生成(如每100个事件),并持久化至
snapshot_store
加载流程(mermaid)
graph TD
A[LoadAggregateByID] --> B{Snapshot exists?}
B -->|Yes| C[Load Snapshot]
B -->|No| D[Load All Events]
C --> E[Filter Events > Snapshot.Version]
E --> F[Apply Incremental Events]
D --> F
Go核心实现片段
func (r *AggregateRepo) Load(id string) (*Order, error) {
snap, ver, err := r.snapshotStore.GetLatest(id) // 返回快照实体及对应事件版本号
if err == nil && snap != nil {
agg := snap.ToAggregate() // 快照反序列化为聚合根
events, _ := r.eventStore.FindAfter(id, ver) // 获取ver之后所有事件
for _, e := range events {
agg.Apply(e) // 严格按序应用事件
}
return agg, nil
}
// fallback: 全量加载
}
snapshotStore.GetLatest()返回(snapshot, eventVersion, error),其中eventVersion是生成快照时已包含的最新事件序号;eventStore.FindAfter()基于id + version范围查询,确保事件不重不漏。
4.4 事件最终一致性保障:Saga模式在Go微服务中的结构化编排——通过go.opentelemetry.io/otel trace透传补偿链路
Saga 模式将长事务拆解为一系列本地事务与对应补偿操作,依赖分布式追踪实现跨服务的补偿链路可观察性。
数据同步机制
核心在于 trace.SpanContext 在 Saga 各参与方间透传,确保正向执行与补偿调用共享同一 traceID 和 parentSpanID。
// 在Saga协调器中启动根Span,并注入context
ctx, span := tracer.Start(ctx, "saga-orchestration")
defer span.End()
// 将span context注入下游HTTP请求头
carrier := propagation.HeaderCarrier{}
propagator.Inject(ctx, carrier)
req.Header.Set("traceparent", carrier.Get("traceparent"))
逻辑分析:
tracer.Start()创建根 Span 标识整个 Saga 流程;propagator.Inject()将 W3C traceparent 编码注入 HTTP 头,使各子服务能延续同一 trace 上下文。参数ctx携带当前 span 状态,"saga-orchestration"为语义化操作名,便于链路聚合分析。
补偿链路可视化关键字段
| 字段 | 用途 | 示例值 |
|---|---|---|
trace_id |
全局唯一标识整个 Saga 流程 | 0af7651916cd43dd8448eb211c80319c |
span_id |
当前步骤唯一标识 | b7ad6b7169203331 |
saga.step |
自定义属性,标记Saga阶段 | "reserve_inventory" / "compensate_payment" |
graph TD
A[Order Service] -->|traceparent| B[Inventory Service]
B -->|traceparent| C[Payment Service]
C -->|traceparent| D[Compensator]
D -->|span link| A
第五章:五套可复用Go-DDD架构图谱全景呈现
核心分层契约与模块边界定义
在真实电商履约系统重构中,团队基于 Go 1.21+ 和 DDD 战略设计,确立了四层严格契约:interface(HTTP/gRPC入口)、application(用例编排与事务边界)、domain(实体/值对象/领域服务/领域事件)、infrastructure(仓储实现、消息推送、第三方适配)。各层通过接口抽象解耦,例如 OrderRepository 接口声明于 domain 层,其实现 mysql.OrderRepository 位于 infrastructure/mysql 包内,编译期强制隔离依赖流向。
五套架构图谱对照表
| 图谱编号 | 典型场景 | 领域事件传播机制 | 基础设施适配粒度 | Go 工程结构特征 |
|---|---|---|---|---|
| A | 单体高并发订单创建 | 同步内存事件总线 | MySQL + Redis 单实例 | cmd/, internal/{domain,app,infra} |
| B | 跨域库存扣减(含Saga) | Kafka 异步事件驱动 | 多租户 DB 分片 + Sentinel | services/inventory/, events/ |
| C | SaaS 化多租户计费引擎 | NATS JetStream 流处理 | TimescaleDB + Stripe SDK | tenant/, billing/domain/ |
| D | 边缘IoT设备指令下发 | MQTT QoS1 + 本地队列回溯 | SQLite嵌入式+蓝牙BLE适配器 | edge/, device/infra/bluetooth/ |
| E | AI模型训练任务编排平台 | Temporal Workflow 事件 | MinIO + Kubernetes Job | ml/orchestrator/, temporal/adapter/ |
领域事件生命周期可视化
stateDiagram-v2
[*] --> EventPublished
EventPublished --> EventStored: infrastructure.EventStore.Save()
EventStored --> EventDispatched: eventbus.Publish()
EventDispatched --> EventHandled: handler.Handle()
EventHandled --> [*]
EventPublished --> EventFailed: err != nil
EventFailed --> RetryPolicy: backoff.Retry()
RetryPolicy --> EventPublished: max_retries < 3
领域服务与基础设施适配器协同模式
以支付网关调用为例:domain.PaymentService.Process() 不直接调用 alipay.Client.Do(),而是接收 payment.Gateway 接口参数;infrastructure/alipay/gateway.go 实现该接口,内部封装签名生成、HTTP Client 重试、熔断器(使用 go-resilience)及日志追踪(OpenTelemetry context 注入)。该模式使单元测试可注入 mock.Gateway,覆盖率稳定维持在 92%+。
Go Module 依赖图谱约束实践
在 go.mod 中强制声明 replace github.com/yourcorp/payment => ./infrastructure/payment,配合 golangci-lint 的 goconst 与 import-shadow 规则,阻断 domain 层对 infrastructure 的隐式引用。CI 流水线中执行 go list -f '{{.ImportPath}}' ./... | grep 'domain.*infrastructure' || true 确保零违规输出。
仓储实现的泛型抽象演进
从早期硬编码 UserRepo.FindByID(id int) (*User, error) 迭代为泛型接口:
type Repository[T Entity, ID comparable] interface {
Save(ctx context.Context, entity T) error
FindByID(ctx context.Context, id ID) (T, error)
Delete(ctx context.Context, id ID) error
}
infrastructure/postgres/user_repo.go 实现 Repository[User, uuid.UUID],复用率提升 67%,且支持 PostgreSQL JSONB 字段自动映射至 User.Preferences map[string]interface{}。
生产环境可观测性集成路径
每套图谱均预置 OpenTelemetry Tracer 初始化逻辑:application 层用例函数入口注入 trace.SpanFromContext(ctx),infrastructure 层 HTTP 客户端自动注入 http.Header 中的 traceparent;Prometheus metrics 按 domain_event_published_total{type="OrderCreated"} 标签维度暴露,Grafana 仪表盘直接关联 Jaeger 追踪 ID。
架构决策记录(ADR)模板固化
所有图谱均配套 adr/001-go-ddd-layering.md,明确记录“选择四层而非六层”的依据:避免 application 与 domain 职责重叠,拒绝引入 service 层导致贫血模型风险;同时规定 infrastructure 层禁止出现 if env == "prod" 分支,必须通过 config.Provider 抽象统一注入。
代码生成器支撑快速图谱落地
基于 entgo.io 和自研 dddgen 工具链,输入领域模型 YAML:
entity: Order
attributes:
- name: Status
type: string
constraints: [enum: "created,paid,shipped"]
events:
- name: OrderPaid
payload: { amount: int64, currency: string }
一键生成 domain/order.go、application/order_usecase.go、infrastructure/kafka/order_event_publisher.go 及对应测试桩。某金融客户两周内完成 14 个核心领域模块骨架搭建。
