传统架构迁移
本页适用于尚未拥有 Wow 事件历史的系统。安全路径是渐进式:确定一个 bounded context,构建模型,在旧 系统保持权威时完成导入与 shadow,先切读,再切写。任何时刻都必须只有一个系统拥有业务写入。
已经使用 Wow v6 的系统应改用 Wow v6 迁移到 v8。
迁移总览
| 阶段 | 权威 writer | 交付物 | 完成证据 |
|---|---|---|---|
| 0. 边界 | Legacy | aggregate/ID/tenant/invariant mapping 与验收场景 | 领域负责人批准范围和语言 |
| 1. Wow 模型 | Legacy | command、aggregate 行为、domain event、state sourcing、测试 | 成功/拒绝/幂等场景通过 |
| 2. 导入与追平 | Legacy | 可重放导入命令、outbox/CDC feed、source watermark | lag 与逐 aggregate 对账满足阈值 |
| 3. 切读 | Legacy | 面向受控 cohort 的 Wow query/read model | 业务结果与延迟对账,回滚已测试 |
| 4. 切写 | Wow | admission 开关、已排空 legacy writer、Wow command path | 单写者证据、新写入对账、回滚可执行 |
| 5. 收尾 | Wow | 观察窗后移除 legacy write/synchronizer | 无未解决 drift 或回滚依赖 |
不要先把表复制到 event store。事件记录的是已接受的领域决策,历史转换必须经过显式、可评审的契约。
1. 先迁移边界,不先迁移表
选择低耦合业务能力,并写清:
- 稳定 aggregate identity、tenant/owner/space mapping 与 aggregate boundary;
- 边界接受哪些 command,谁有权发送;
- 哪些 invariant 会拒绝 command;
- 接受后产生哪些 domain event;
- state sourcing 与删除语义;
- 哪些外部调用属于 projection/Saga,而不是 aggregate state transition。
遵循仓库示例结构:API command/event 与 aggregate 实现分离,AggregateSpec 在无基础设施情况下验证行为。
class OrderSpec : AggregateSpec<Order, OrderState>({
on {
whenCommand(CreateOrder(id = "order-1", ...)) {
expectNoError()
expectEventType(OrderCreated::class)
expectState { id.assert().isEqualTo("order-1") }
}
}
})还要覆盖重复 request ID、非法 transition、已删除 aggregate、tenant/owner 传播,以及实际提交 event shape 的序列化。Aggregate 能编译只属于源码证据,不能证明历史行能映射成合法领域决策。
通过 anti-corruption adapter 把 legacy contract 转为 Wow command。除非确实是业务概念,不要把旧 column name 与 sentinel value 带进新的公开领域 API。
2. 用单写者完成历史导入与增量追平
使用耐久、可恢复的迁移 manifest,至少记录 source partition/key、source version/update watermark、target aggregate ID、确定性 request ID、状态、source checksum、target version/checksum、错误与最后验证 batch。
推荐流程:
- 在 legacy 写入继续时取得 source snapshot,并记录 high watermark;
- 通过显式
Import.../Synchronize...command contract 转换每个 source entity; - 使用确定性 request ID,确保已完成 batch 可幂等重放;
- 只有获得 target acknowledgement 并验证后,才持久化 manifest 进度;
- 从 snapshot watermark 之后按 source 顺序消费 outbox/CDC 变化;
- 重复对账,直到 lag 持续满足批准阈值。
source row 只是定位器;经过评审的 mapping 决定它代表什么 Wow command/event/state。保留 source payload/ checksum 作为审计证据,不要把转换选择隐藏在临时脚本里。
禁止一次请求分别独立提交 legacy database 与 Wow 的“双写”。使用 outbox/CDC 或其他耐久 handoff。 synchronizer 失败时,legacy 仍是唯一 writer,target 从最后 manifest/watermark 继续追平。
对账至少要逐 aggregate 和全局比较:
- source population 与 imported ID,包括 missing/extra ID;
- 最新 source revision/update time 与 target aggregate version;
- 金额、数量、状态、删除等业务 invariant;
- 重复确定性 request ID 与 failed/dead-letter 记录;
- event/snapshot 数量、代表性完整 replay 与 read-model 行。
3. 对账后分别切换读与写
条件允许时先切读:
- 用同一请求 shadow-query legacy 与 Wow read model;
- 把差异分类为 mapping defect、预期语义变化、lag 或数据损坏;
- writer 仍由 legacy 拥有时,切换受控 read cohort;
- 观察 error rate、latency、lag、业务结果、metric 与 trace;
- 门禁失败时只回滚读,不改变 writer。
写切换是单独维护动作:
- 关闭 legacy write admission,排空 in-flight transaction/outbox record;
- 记录最终 source watermark,批准范围内的 reconciliation drift 必须为零;
- 只为一个受控 cohort/instance 开启 Wow command boundary;
- 验证 command result、committed event、reconstructed state、projection/Saga 与外部 side effect;
- 观察门禁通过后才扩大流量。
第一次 Wow 生产写入前,可把读取切回 legacy 并丢弃/重建 shadow target。第一次写入后,回滚必须停止 Wow,把这些写入传回或反向转换到 legacy authority,并重新对账两侧。只恢复旧应用会丢失已接受决策。
本地测试、导入演练成功与 canary 健康是三类不同证据。生产准入还要求已审批 image/revision、真实 routing、 monitoring/alert 与明确 incident owner。
4. 领域模型继续演进
切换后通过新 command/event 与显式序列化契约演进;不要仅为了让当前 class shape 更方便而改写 committed event history。
每次变更都应:
- 证明旧 event 仍能反序列化并 source 成预期当前 state,或提供已评审离线历史转换;
- 使用新 event 显式表达新语义,不要静默改变已有 field 含义;
- derived shape 变化时从 event 重建 snapshot 与 projection;
- 删除旧 field/migration adapter 前对账 state/read model;
- 固定 rollback version,并决定它如何处理新版本写出的 event。
snapshot 或 projection 重建成功不能修复错误 event mapping;代表性和边缘 replay 必须验证业务 state。
完成检查清单
- [ ] bounded context、aggregate identity、tenant/owner mapping、invariant 与排除项已批准
- [ ] command/event/state 契约及 AggregateSpec 拒绝/幂等场景通过
- [ ] import/CDC manifest 耐久、可恢复、确定性
- [ ] 每个阶段都能证明只有一个权威 writer
- [ ] source/target 数量、版本、checksum、invariant、replay 与 read model 已对账
- [ ] 读切换与写切换分别完成演练
- [ ] 第一次 Wow 写入前后的回滚都已演练
- [ ] deployed revision、真实流量、metric/trace、alert 与业务检查通过
- [ ] 观察窗关闭前保留 legacy write 与临时同步链路
相关页面
| 页面 | 关系 |
|---|---|
| 迁移指南 | 范围与共同证据门禁 |
| 聚合与不变量 | Aggregate 与 command/event 设计 |
| 测试 | 领域行为验证 |
| 商业智能 | 可重建的分析读模型 |
| Wow v6 迁移到 v8 | 既有 Wow 版本升级 |