跳到正文

入门

核心闭环

go-sequent 写路径逐步说明:CommandService.Execute、中间件、AuthZ、Handler、Repository.Commit、SyncInTxn 发布、事务提交与 AfterCommit。

核心闭环

go-sequent 里每一次状态变更都走同一个入口:CommandService.Execute(ctx, cmds...)。弄清它内部做了什么,就弄清了这个库的大半。

客户端 / 服务入口
 │
 ▼
 CommandService.Execute(ctx, cmds...)
 │ 通过 WithinTx 开启事务,ctx 上挂 UnitOfWork
 │
 │ 对每条命令:
 ├─ EnsureMeta
 ├─ Middleware 链(Tenancy、Idempotency……)  外层先执行
 ├─ Filters(AuthZ)
 ├─ Command.Validate
 ├─ Router.Dispatch → CommandHandler
 │     Repo.Add / Repo.Load → 聚合方法 → Apply(event)
 ├─ Repository.Commit
 │     EventStore.Append  (OCC、唯一键、hash chain)
 │     Publisher.Publish  → Projectors → Workflows
 ▼
 事务提交 → AfterCommit 钩子

你写什么,库做什么

你编写 Command、Handler、Aggregate、Event 与 Projector(把事件写成查询用读模型,例如余额表)。其余由库提供:command.Service、aggregate.Repository、事件存储与 publish.SyncInTxn。

步骤 负责方 发生了什么
事务与 UnitOfWork 库 Execute 经存储的 TxManager 开事务,并把 UnitOfWork 放进 context。
Middleware 你选择 常见是 Tenancy 与 Idempotency,外层优先执行。
Filters 你选择 AuthZ 在这里跑;被拒绝就终止该命令。
Validate 你 检查命令自身形状。
Handler 你 加载或创建聚合,调用其方法。
Aggregate 你 执行规则,然后 Apply 事件。
Commit 库 追加被改过的聚合事件;强制 OCC(用 ExpectedVersion 防并发写冲突)、唯一键与 hash chain。
发布 库 同一事务内先跑 Projector,再跑 Workflow(SyncInTxn:在写事务里同步更新读模型)。
AfterCommit 你 经 workflow.RegisterAfterCommit 注册的钩子在事务提交后跑。

为什么是一个事务

事件、唯一键、Idempotency 记录与账本读模型要么一起提交,要么都不提交。某个 Projector 返回错误,事件就不会落库。这是 SyncInTxn 的保证,也是 Execute 返回后立刻读到的余额不会过期的原因——提交返回后立刻能读到自己刚写的数据。

不能跟着事务回滚的副作用(例如发邮件)应放进 AfterCommit,而不是 Projector。

一次调用中的多条命令

Execute 接受一个列表。所有命令共享事务和 UnitOfWork,因此要么全部提交,要么全部不提交。两条命令必须原子执行时用它。涉及两个聚合的一次业务操作,通常用一条同时加载两者的命令更清晰,见 领域建模里的转账。

Context 级 UnitOfWork

UnitOfWork 放在 context.Context 里,每次 Execute 一个。不要跨 goroutine 共享。彼此独立的命令走各自独立的 Execute。

各步骤的文档位置

文档随 go-sequent 源码更新;若与 API 不一致,以源码为准。