参考
相对 Sequent Ruby
go-sequent 与 Ruby 版 Sequent:概念对照、Go 已实现部分、差异(context 级 UnitOfWork、decimal 金额、hash chain),以及刻意不移植的内容。
相对 Sequent Ruby
Sequent 是 Ruby 上有明确主张的 CQRS + Event Sourcing 框架。go-sequent 借用它的概念与词汇,为金融账本场景做一个 Go 原生库。本页说明两者如何对应、又在哪里分道扬镳。
go-sequent 不是移植版,也不兼容其线协议或数据库。它永远不会读取或导入 Ruby Sequent 的数据库。
概念映射
| Sequent(Ruby) | go-sequent |
|---|---|
Sequent::Command + attrs + ActiveModel 校验 |
Command 接口;命令上的 Validate() error |
Sequent::Event + attrs |
Event 接口;结构体嵌入 event.Base,按类型名注册 |
Sequent::AggregateRoot + on Event do |
嵌入 aggregate.Base;On(sample, applier);命令方法调用 Apply |
CommandHandler + on Command do |
带 Handles() 与 Handle() 的 command.Handler,按命令类型字符串路由 |
CommandService + 线程本地队列 |
command.Service;每次 Execute 一个事务和一个 context 级 UnitOfWork |
AggregateRepository(线程 UoW、identity map) |
绑定到 context 上 UnitOfWork 的 aggregate.Repository |
EventStore + PL/pgSQL store_events |
eventstore.Store;Postgres 实现沿用同样的加锁与序号逻辑 |
Projector + Persistor |
带 ManagedTables 的 projector.Projector 与 projector.Persistor(把事件写成查询用读模型) |
Workflow + after_commit |
workflow.Workflow 与 workflow.RegisterAfterCommit |
Sequent.configure 全局配置 |
显式构造的 sequent.New 与 sequent.App |
| 命令中间件与过滤器 | command.Middleware 链与 command.Filter |
| 基于 Marshal 的快照 | 带版本的 JSON 快照 |
| 唯一键 | 聚合上的 UniqueKeys(),存入 aggregate_unique_keys |
| 迁移与 projector replay | go-sequent migrate 与 projector/replay(prepare、replay、catchup、optimize、golive) |
| Handler 自动注册 | 显式的 Register* 调用 |
已实现的部分
用 Go API 实现:AggregateRoot、Command、Event、CommandService、Repository、EventStore、Projector、Workflow、快照、upcaster、迁移与 replay 流水线。
基本流程相同:一条命令开启一个事务;Handler 通过 Repository 加载或创建聚合,聚合应用事件;Repository 提交事件,并默认在同一事务里发布给 Projector 与 Workflow。
go-sequent 的不同之处
并发模型
Ruby Sequent 的 UnitOfWork 是每线程一个。Go 把 UnitOfWork 放在 context.Context 里,每次 Execute 一个,禁止跨 goroutine 共享。并行来自彼此独立的请求、重建 worker 与异步消费者,而不是单条命令内部。
金融相关能力
以下超出了 Sequent,在 go-sequent 里是一等公民:
- decimal 的
types.Money,并禁止float64金额字段。 - 租户级 Idempotency 键(同一业务键重复提交只生效一次)。
- 命令边界的 AuthZ,含
DenyByDefault。 - 共享 schema +
tenant_id的多租户,以及UniqueScope、PartitionKey辅助函数。 - 对事件按聚合维护的 hash chain(按聚合串联事件哈希,用来发现篡改)。
- 通过
Meta与AuditOf为每条命令提供审计元数据。 - GDPR 的 Tombstone(标记清除事件:不删历史事件,而是追加一条标记事件,让读模型不再暴露个人数据)钩子。
- 写路径上可选的 OpenTelemetry。
- 可选的、由 Postgres 轮询消费的 Outbox(事务内记待发消息,事后轮询投递)。
发布
Ruby Sequent 默认在写事务内同步运行 Projector。go-sequent 保留这一点,称为 SyncInTxn(写事务内同步更新读模型),并要求账本使用它。异步路径使用从 Postgres 表轮询的 Outbox。没有 LISTEN/NOTIFY 模式,也没有消息总线。
事件完整性
go-sequent 把每个已存储事件哈希成一条链,但不对事件签名。Sequent 没有对应的机制。
不移植的部分
| Sequent(Ruby) | go-sequent |
|---|---|
| ActiveRecord Records 与迁移 DSL | 没有。Projector 声明 ManagedTables,通过 projector/replay 重建读模型。 |
| Rails / Sinatra 生成器与自动注册 | 没有,这是纯库。 |
| AggregateObserver | 未提供 |
| ValueObject 类型系统 | 普通 Go 结构体加 types.Money |
| 默认消息总线、LISTEN/NOTIFY | 可选的 Outbox 轮询 |
Sequent::Secret(BCrypt) |
不是框架功能;密码哈希放在你的应用里 |
| 事件签名 | 不在范围内;hash chain 提供篡改检测 |
| 导入 Sequent 数据库 | 永不 |
| HTTP 或 gRPC | 没有 |
从 Ruby Sequent 系统迁移
没有从 Ruby Sequent 数据库迁数据的路径。若要迁系统,可以用自己的工具向 go-sequent 发命令重放业务历史,或让两个系统并行运行,在业务边界上切换。不要把 Ruby Sequent 的 SQL dump 加载到 go-sequent 的 schema 里。
相关
文档随 go-sequent 源码更新;若与 API 不一致,以源码为准。