# 贯穿案例设计：分布式订单履约控制面

## 1. 故事目的

贯穿案例用一个业务上下文解释三种不同的一致性诉求。案例不追求构造完整电商系统，而是持续回答一个问题：哪些协同状态适合 DSM，哪些业务事实必须留在事务系统中。

## 2. 系统边界

### DSM 边界内

| 状态 | 集合 | 业务用途 | 丢失后的恢复方式 |
| --- | --- | --- | --- |
| `route-hints` | Register | 告诉履约节点某个服务当前应访问的地址与健康提示 | 从健康检查或服务注册源重新发布，或从同伴修复 |
| `shard-owners` | Lease | 让一个 worker 在租期内拥有订单分片处理权 | 租约到期后重新选主；下游写入使用 fencing token 防止旧 owner |
| `request-counter` | CRDT | 合并各节点本地处理请求数 | 从同伴状态合并；必要时从业务指标系统重新校准 |

### DSM 边界外

| 业务事实 | 事实源 | 不进入 DSM 的原因 |
| --- | --- | --- |
| 订单主记录与状态迁移 | 事务数据库 | 需要事务约束、审计历史和确定的写入结果 |
| 库存扣减 | 库存系统或事务数据库 | 超卖约束不能由最终收敛替代 |
| 支付与退款账务 | 账务系统 | 需要不可抵赖记录、对账和监管控制 |
| 业务事件历史 | 事件存储或消息系统 | DSM repair 修复当前协同状态，不保证完整历史重放 |

## 3. 角色

| 角色 | 职责 | 不承担的职责 |
| --- | --- | --- |
| Fulfillment API | 接收履约请求，读取路由并提交订单数据库事务 | 不直接管理 DSM 生命周期 |
| Fulfillment Worker | 领取分片租约，处理属于该分片的订单 | 没有有效 fencing token 时不能写入下游 |
| Route Publisher | 把健康检查结果转成 `RouteHint` 并发布 | 不替代服务注册系统的事实源 |
| Metrics Adapter | 把 `request-counter` 和 DSM 指标暴露给观察系统 | 不把计数器当账单或财务数据 |
| DSM Runtime | 保存集合、盖元数据、发出变更并应用远端更新 | 不决定业务订单是否成功 |
| Sync / Repair | 传播 delta，并在副本落后时执行 replay 或 snapshot | 不撤销已经返回给客户端的业务响应 |

## 4. 核心不变量

### 4.1 Register 不变量

- 相同 locator 和 entry key 的多个候选值必须通过确定性规则得到同一可见结果。
- `put` 的本地成功不等于所有节点已经可见。
- 自定义冲突 resolver 必须满足交换律；测试中使用 `ConflictResolver.verified(...)` 检测反例。

### 4.2 Lease 不变量

- 有效租期内只接受当前 owner 的续租、转移或释放。
- owner 变更后，旧 owner 即使仍在运行，也不能凭旧 fencing token 写入受保护下游。
- 成员视图不稳定时，QUORUM 模式拒绝 acquire、renew 和 transfer，避免用漂移的多数派作决定。

### 4.3 CRDT 不变量

- 每个节点可以本地更新 `request-counter`。
- 相同更新集合按不同顺序合并，结果必须等价。
- 重复 delta 不能重复放大结果；repair 传输的是可合并状态，不是业务事件历史。

### 4.4 业务边界不变量

- DSM 收敛不证明订单数据库事务成功。
- 分区期间两个节点都返回局部成功时，repair 只决定恢复后的可见状态，不撤销已经返回的响应。
- 计数器只用于运行观察，不能直接作为计费、结算或库存事实。

## 5. 故事演进

| 章节范围 | 故事状态 |
| --- | --- |
| 1—4 | 一个节点发布 route hint，第二个节点看到复制结果 |
| 5—9 | 并发路由更新暴露冲突；增加分片 Lease 和请求 CRDT；完成集合选择 |
| 10—14 | 节点加入、delta 丢失、repair 和分区窗口使一致性边界可观察 |
| 15—18 | 用领域适配器封装集合，在 Spring Boot 中装配并建立测试证据 |
| 19—22 | 增加诊断、安全、生命周期和容量验证 |
| 23—26 | 用 Redis-shaped lab 检验边界，理解 Federation，完成选型与毕业项目 |

## 6. 叙事约束

- 不在第 7 章之前使用 Lease 解决 route hint 更新。
- 不在第 8 章之前用 CRDT 掩盖 Register 冲突。
- 不在第 12 章之前假设读者理解 repair 内部选择。
- 不把订单业务结果写入 `route-hints`、`shard-owners` 或 `request-counter`。
- 新增业务动作前，先说明它需要哪一种不变量以及失败后如何恢复。

## 7. 毕业项目交付

毕业项目至少包含：

1. 三个集合的实体、codec、spec 和领域适配器。
2. 两节点启动与 route hint 复制证据。
3. Lease acquire、renew、失效与 fencing 测试。
4. 两节点 CRDT 本地更新和合并证据。
5. 一次 delta 漏失或迟到节点 repair 演练。
6. 诊断、指标和限制说明。
7. 对订单、库存、支付和协同状态的最终选型表。
