跳到正文

参考

相对 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 不一致,以源码为准。