[REQ-20260824-0017] Add req-dev read-model migration gate #9

Merged
qiudl merged 1 commits from docs/req-dev-read-model-migration-gate into main 2026-08-25 19:57:07 +00:00
+54
View File
@@ -322,6 +322,59 @@ npm run lint
---
## ⚠️ 既有契约与读模型迁移门禁(必须)
> **惨痛教训**:新写链路把真实数据写入新表,旧 API 仍读原表,前端虽然没改却从此看不到新数据。“后端已成功执行”不等于“用户能看到执行结果”。
开发计划涉及新表、新账本、新写路径、替换存储模型或接口版本时,必须先完成以下门禁。未通过时,不得把“新建平行表/新建平行 API”写成默认方案。
### 1. 先证明旧模型不能复用
- [ ] 已检查现有表、字段、索引、状态机和扩展字段(如 metadata/JSON)。
- [ ] 已说明为什么增加字段、子表或现有类型无法满足需求。
- [ ] 新表承载的是真正独立的实体或生命周期,而不是为同一业务实体建立第二个彼此脱节的世界。
- [ ] 已区分“内部不可变证据/事件账本”和“产品对外读模型”;新建内部账本不代表必须更换原有产品接口。
### 2. 真实调用方查证
必须从对外接口向所有消费方反向追踪,不能只审查新后端写路径:
- [ ] 列出现有 API 路径、请求参数、响应结构和身份/分页语义。
- [ ] 使用 `rg` 查找 Web、iOS、Android、定时任务、报表和第三方调用方。
- [ ] 若仓库存在 `.codex-fe-ref/`,必须在其中查证配套前端的真实调用。
- [ ] 开发计划的变更文件清单必须覆盖“写入 → 投影/查询 → API → 页面”完整链路。
### 3. 稳定契约默认不变
- 既有用户界面已消费的 API,默认保留路径和响应契约;除非用户明确批准破坏性迁移,不得通过新建版本接口把兼容成本转嫁给前端。
- 同一业务概念只保留一个产品级读模型。内部账本可以拆分,但必须投影回既有读模型,或由原 API 无感聚合。
- 日志、告警、信号或推测数据不得在查询降级时冒充业务事实(例如把 warning 当成交易记录)。
### 4. 不可避免新写模型时的强制迁移计划
若确有独立生命周期、不可变审计或 1:N/N:N 证据需求,可以增加新表,但开发计划必须同时包含:
1. **写入兼容**:双写,或从新账本向既有读模型做事务性/幂等投影。
2. **历史回填**:有界、可恢复、可重跑,且不猜测无法证明的归属。
3. **去重身份**:定义跨旧/新数据源的稳定业务键,并用数据库唯一约束或等价强保证防重。
4. **读取切换**:定义何时以新证据为准、旧数据如何兼容,以及结果数量/金额/状态对账门槛。
5. **回滚策略**:回滚新写入时仍能读取已产生的真实业务事实,不删除或隐藏已成功数据。
6. **可观测性**:监控新写入成功但旧读模型缺失、投影延迟、去重冲突和回填差异。
### 5. 用户可见性是必须验收项
测试不得只证明“新写路径成功”。至少要有一个真实业务记录贯穿测试,同时证明:
- [ ] 新写路径产生了正确事实。
- [ ] 原有对外 API 可以立即或在约定 SLA 内返回该记录。
- [ ] 每个真实消费页面/客户端都能正确解码并展示。
- [ ] 时间、方向、状态、数量、金额与唯一标识在数据库、API 和 UI 之间一致。
- [ ] 部分失败、重试、重复投递、迟到数据和回滚后仍不丢数、不重复、不冒充。
开发计划必须把上述内容落成明确的变更文件、任务、测试用例和发布门禁,不得只在“风险”章节留一句提醒。
---
## 开发计划文档模板
```markdown
@@ -1695,3 +1748,4 @@ migrate -path migrations -database "postgres://..." up
|------|------|----------|--------|
| V1.0 | 2026-01-26 | 初始版本,创建 req-dev 技能 | Claude + qiudl |
| V1.1 | 2026-01-29 | 添加 iOS 端开发规范(SwiftUI + MVVM 架构) | Claude Opus 4.5 |
| V1.2 | 2026-08-26 | 新增既有契约与读模型迁移强制门禁,防止新写链路与原 API/前端脱节 | Codex |