适用范围
约束所有持久化对象:可变性、顺序、来源归属、并发保护、保留与迁移。
不约束:缓存与派生视图的内部形状;实现仓的列类型与索引选择;单一功能私有且无审计价值的临时数据。
规范条款
可变性分类
- 每个持久化对象必须被明确归类为下列之一,并必须在功能的设计文档中声明:
| 分类 | 语义 | 允许的操作 |
|---|
| 只追加 | 写入后不可改写、不可删除 | 只能新增 |
| 不可改写 | 一经写入即拒绝任何更新 | 写入一次 |
| 状态可变 | 允许状态迁移 | 只能按声明的状态机迁移 |
| 可编辑 | 允许更新 | 更新,且必须保留审计 |
- 归类为只追加或不可改写的对象,必须在存储层拒绝更新;必须不只靠应用层约定。
- 需要保留"当时是什么"的历史事实,必须用只追加记录表达;必须不用可编辑字段覆盖。
顺序与并发保护
- 每个状态可变对象必须具备单调递增的修订,用于并发保护与顺序判定。
- 修订必须从 1 开始、单调递增,必须不回退或复用。
- 变更请求必须携带期望修订;与当前不符时必须失败,必须不静默覆盖。
- 进度类字段(已观察 / 已结算 / 已应用)必须满足单调序:已结算 ≤ 已观察 ≤ 当前修订。
- 唯一性约束必须落在存储层;必须不只靠应用层检查。
- 需要"同一作用域最多一个活跃对象"时,必须用部分唯一约束(条件唯一)表达;必须不用"先查后写"。
来源归属
- 每条记录必须能回答"是谁写的";同一对象存在多个 writer 时必须用显式来源字段区分。
- 来源字段取值必须封闭(枚举);必须不允许未定义来源写入。
- 各来源所需字段必须由约束保证齐备(例如平台侧写入不得携带外部身份字段,外部写入必须携带全部身份字段);平台侧与外部必须不能互相冒充。
不可变性
- 不可改写对象在写入后必须拒绝任何更新,且必须给出可诊断错误;必须不静默忽略。
- 终态结果、审计事件、控制命令必须属于只追加或不可改写类别。
- 撤销必须用新增记录表达(补偿);必须不通过改写历史实现。
保留与删除
- 删除必须区分软删除(可恢复、保留审计)与硬删除(不可恢复)。
- 有审计价值的记录必须只允许软删除;硬删除必须停在明确批准门之前。
- 软删除后是否参与唯一性约束必须显式声明。
- 结构变更必须通过版本化迁移表达;必须不手工改库。
- 迁移必须能在干净库上依次应用;破坏性迁移(删列、删表、改语义)必须有独立批准,并必须说明历史数据处置方式。
- 已终态或只追加数据必须不被迁移改写。
违反时的失败语义
| 违反情形 | 必须的行为 |
|---|
| 未声明可变性分类 | 视为规格违反;评审阶段必须拦截 |
| 唯一性只靠应用层检查 | 必须改为存储层约束;否则并发下必然失效 |
| 期望修订缺失 | 必须拒绝变更,避免静默覆盖 |
| 来源字段缺约束 | 必须补约束;否则无法判定"谁写的" |
| 改写不可变对象 | 必须拒绝并记录诊断 |
| 破坏性迁移无批准 | 必须停止执行 |
校验方式
| 校验对象 | 判据 | 失败表现 |
|---|
| 可变性归类 | 每个持久化对象必须能定位到其分类声明 | 缺失即失败 |
| 不可变性强制 | 对不可改写对象发起更新必须被拒绝 | 被接受即失败 |
| 存储层唯一性 | 并发写入必须只能有一个成功 | 两个都成功即失败 |
| 部分唯一约束 | 同作用域第二活跃对象必须被存储层拒绝 | 被接受即失败 |
| 修订单调 | 构造修订回退或复用必须被拒绝 | 被接受即失败 |
| 期望修订 | 以陈旧修订提交必须失败 | 成功即失败 |
| 进度单调 | 构造"结算超过观察"必须被拒绝 | 被接受即失败 |
| 来源齐备 | 各来源缺所需字段必须被拒绝 | 被接受即失败 |
| 迁移可前进 | 迁移必须能在干净库上依次应用 | 失败即失败 |
相关规格