返回博客

Schema 变更管理:不停机的 Ontology 演进

噩梦场景:

Coomia发布于 2025年8月13日21 分钟阅读
分享本文Twitter / X

Schema 变更管理:不停机的 Ontology 演进

系列:S4 本体建模 · 第 9 篇 | 难度:中级 | 阅读时间:18 分钟

#TL;DR

  • Ontology Schema 不是一成不变的——业务演进意味着 ObjectType 需要新增属性、修改类型、删除字段,coomia-dip 提供 Schema 版本化和安全迁移机制,确保变更不会打破现有数据和消费者。
  • **三阶段迁移协议(Propose → Validate → Apply)**让每次 Schema 变更都经过兼容性检查、影响分析、数据迁移验证,杜绝"改了 Schema 就炸了"的事故。
  • 向后兼容性规则自动判定——新增属性(安全)、修改类型(需验证)、删除属性(危险),系统自动分类并给出迁移建议。

#1. 为什么 Schema 变更管理至关重要

#1.1 没有版本控制的 Schema 变更

Code
噩梦场景:

周一:DBA 把 Order.status 从 STRING 改成了 ENUM
周二:报表团队的 ETL 管道全部失败——他们用 status LIKE '%active%'
周三:前端团队发现下拉菜单不显示了——API 返回的枚举值和前端不匹配
周四:数据科学团队的模型训练失败——特征列的数据类型变了
周五:回滚!但回滚也炸了——新写入的 ENUM 值无法转回 STRING

根因分析:
├── 没有变更影响分析
├── 没有通知下游消费者
├── 没有数据迁移计划
├── 没有回滚方案
└── 没有兼容性检查

#1.2 coomia-dip 的 Schema 版本化

Code
coomia-dip 的 Schema 变更流程:

┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐
│ Propose │ →  │Validate │ →  │ Preview │ →  │  Apply  │
│ 提出变更 │    │兼容性检查│    │影响预览  │    │ 执行迁移 │
└─────────┘    └─────────┘    └─────────┘    └─────────┘
                    │                              │
                    ↓                              ↓
              不兼容?拒绝                    自动数据迁移
              或要求显式确认                  + 版本号递增

每个 ObjectType 有版本历史:
  Order v1: {orderId, status: STRING, amount: DOUBLE}
  Order v2: {orderId, status: ENUM, amount: DOUBLE, createdAt: TIMESTAMP}
  Order v3: {orderId, status: ENUM, totalAmount: DOUBLE, createdAt: TIMESTAMP}
            ↑ amount 重命名为 totalAmount

#2. Schema 变更的分类

#2.1 变更类型矩阵

Code
变更类型与风险评级:

┌──────────────────┬────────┬──────────────────────────────┐
│ 变更类型          │ 风险    │ 说明                          │
├──────────────────┼────────┼──────────────────────────────┤
│ 新增可选属性      │ 🟢 安全 │ 旧数据不受影响,新数据可选填   │
│ 新增必填属性+默认值│ 🟢 安全 │ 旧数据自动填充默认值           │
│ 新增索引          │ 🟢 安全 │ 后台异步构建,不影响读写       │
│ 属性重命名        │ 🟡 中等 │ 需要更新 API 消费者            │
│ 扩大类型范围      │ 🟡 中等 │ INT→LONG, STRING→TEXT 安全    │
│ 新增枚举值        │ 🟡 中等 │ 旧消费者可能不识别新值         │
│ 修改属性类型      │ 🔴 危险 │ 可能导致数据丢失              │
│ 缩小类型范围      │ 🔴 危险 │ LONG→INT 可能溢出             │
│ 删除属性          │ 🔴 危险 │ 依赖该属性的消费者会失败       │
│ 删除枚举值        │ 🔴 危险 │ 现有数据可能包含该值           │
│ 新增必填属性无默认 │ 🔴 危险 │ 旧数据无法满足约束            │
│ 修改主键          │ ⛔ 禁止 │ 破坏所有关系和引用             │
└──────────────────┴────────┴──────────────────────────────┘

#2.2 兼容性规则引擎

Code
兼容性检查引擎:

输入:当前 Schema + 变更请求
输出:兼容性判定 + 迁移建议

规则 1:新增属性
  IF 属性 nullable == true OR default != null:
    → COMPATIBLE(安全)
  ELSE:
    → BREAKING(危险:旧数据缺少该字段)
    → 建议:添加默认值或设为可选

规则 2:类型变更
  兼容类型对(安全扩展):
    BOOLEAN → STRING       ✓
    INT → LONG → DOUBLE    ✓
    STRING → TEXT           ✓
    DATE → TIMESTAMP        ✓

  不兼容类型对(数据丢失风险):
    DOUBLE → INT            ✗(精度丢失)
    STRING → INT            ✗(格式不匹配)
    TIMESTAMP → DATE        ✗(时间丢失)

规则 3:删除属性
  扫描所有依赖:
    派生属性依赖?→ BREAKING
    ActionType 参数依赖?→ BREAKING
    RelationType 引用?→ BREAKING
    外部 API 消费者?→ WARNING
  IF 无任何依赖:
    → COMPATIBLE(但仍建议先标记 @Deprecated)

规则 4:枚举变更
  新增值:COMPATIBLE(旧消费者可能需要更新)
  删除值:
    IF 现有数据包含该值:BREAKING
    IF 现有数据不包含该值:COMPATIBLE(但需验证)
  修改值名称:BREAKING(本质上是删除旧值+新增新值)

#3. 三阶段迁移协议

#3.1 阶段一:Propose(提出变更)

Code
API: POST /api/v1/ontology/schema/proposals

请求:
{
  "objectTypeId": "Order",
  "changes": [
    {
      "type": "ADD_PROPERTY",
      "property": {
        "name": "priority",
        "dataType": "ENUM",
        "enumValues": ["LOW", "MEDIUM", "HIGH", "URGENT"],
        "nullable": false,
        "defaultValue": "MEDIUM"
      }
    },
    {
      "type": "MODIFY_PROPERTY",
      "propertyName": "amount",
      "modifications": {
        "rename": "totalAmount",
        "dataType": "DECIMAL"  // 从 DOUBLE 改为 DECIMAL
      }
    },
    {
      "type": "DEPRECATE_PROPERTY",
      "propertyName": "legacyCode",
      "deprecationMessage": "Use 'orderCode' instead",
      "removalVersion": "v5"
    }
  ],
  "description": "Add priority field, rename amount to totalAmount with DECIMAL precision",
  "author": "alice@company.com"
}

响应:
{
  "proposalId": "prop-20250115-001",
  "status": "PENDING_VALIDATION",
  "version": {
    "current": "v3",
    "proposed": "v4"
  },
  "changes": [...],
  "createdAt": "2025-01-15T10:00:00Z"
}

#3.2 阶段二:Validate(兼容性检查)

Code
API: POST /api/v1/ontology/schema/proposals/{proposalId}/validate

自动执行的检查项:

┌──────────────────────────────────────────────────────┐
│  Schema Change Validation Report                      │
│  Proposal: prop-20250115-001                          │
│  ObjectType: Order (v3 → v4)                          │
├──────────────────────────────────────────────────────┤
│                                                       │
│  Change 1: ADD_PROPERTY "priority"                    │
│  ├── Compatibility: ✅ COMPATIBLE                     │
│  ├── Reason: Has default value "MEDIUM"               │
│  ├── Data migration: None required                    │
│  └── Impact: None                                     │
│                                                       │
│  Change 2: RENAME "amount" → "totalAmount"            │
│  ├── Compatibility: ⚠️ REQUIRES_MIGRATION             │
│  ├── Reason: Property name change                     │
│  ├── Data migration: Column rename (zero-copy)        │
│  └── Impact:                                          │
│      ├── 3 Derived Properties reference "amount"      │
│      │   → Will auto-update to "totalAmount"          │
│      ├── 2 ActionTypes use "amount" as parameter      │
│      │   → Require manual update                      │
│      ├── 1 RelationType filters on "amount"           │
│      │   → Will auto-update                           │
│      └── External APIs: /api/v1/orders response       │
│          → Add alias "amount" → "totalAmount"         │
│                                                       │
│  Change 3: TYPE_CHANGE "amount" DOUBLE → DECIMAL      │
│  ├── Compatibility: ⚠️ REQUIRES_MIGRATION             │
│  ├── Reason: Type widening (safe direction)           │
│  ├── Data migration: CAST(amount AS DECIMAL(18,4))    │
│  ├── Estimated time: ~30s for 1M rows                 │
│  └── Impact: None (DECIMAL is superset of DOUBLE)     │
│                                                       │
│  Change 4: DEPRECATE "legacyCode"                     │
│  ├── Compatibility: ✅ COMPATIBLE                     │
│  ├── Reason: Deprecation only, not removal            │
│  └── Impact: Consumers will see @Deprecated warning   │
│                                                       │
│  Overall: ⚠️ COMPATIBLE_WITH_MIGRATION                │
│  Estimated migration time: ~35 seconds                │
│  Requires confirmation: YES (rename + type change)    │
└──────────────────────────────────────────────────────┘

#3.3 阶段三:Apply(执行迁移)

Code
API: POST /api/v1/ontology/schema/proposals/{proposalId}/apply

执行过程(分步骤,每步可回滚):

Step 1: 创建新版本 Schema(v4)
  ├── 在 Schema Registry 中注册 v4
  ├── v3 和 v4 同时有效(双版本共存期)
  └── ⏱️ < 1 秒

Step 2: 数据迁移
  ├── 新增列 "priority" DEFAULT 'MEDIUM'
  ├── 重命名列 "amount" → "totalAmount"
  ├── 类型转换 DOUBLE → DECIMAL
  ├── 在线 DDL(不锁表)
  └── ⏱️ ~35 秒

Step 3: 更新派生属性
  ├── 扫描所有引用 "amount" 的派生属性表达式
  ├── 自动替换为 "totalAmount"
  ├── 重新验证表达式合法性
  └── ⏱️ < 5 秒

Step 4: 更新 API 层
  ├── 添加属性别名:amount → totalAmount
  ├── API 同时接受旧名和新名(兼容期)
  ├── 设置兼容期截止日期(默认 30 天)
  └── ⏱️ < 1 秒

Step 5: 发布变更通知
  ├── 通知所有注册的 Schema 变更监听器
  ├── 发送变更日志到审计系统
  ├── 更新 SDK 的类型定义
  └── ⏱️ < 1 秒

总耗时:~42 秒(其中 35 秒是数据迁移)
全程无停机,读写不受影响

#4. 数据迁移策略

#4.1 在线迁移 vs 离线迁移

Code
在线迁移(Online Migration)—— 默认策略:

适用:
├── 数据量 < 1000 万行
├── 变更类型为安全扩展
└── 可以接受轻微性能下降(<10%)

实现方式:
  1. 双写(Double Write)
     新数据同时写入旧格式和新格式
  2. 后台回填(Backfill)
     后台线程逐批迁移历史数据
  3. 切换(Cutover)
     历史数据迁移完成后,切换到新格式
  4. 清理(Cleanup)
     删除旧格式数据

时间线:
  T0: 开始双写
  T0 ~ T1: 后台回填(分钟 ~ 小时)
  T1: 回填完成,验证数据一致性
  T2: 切换到新格式
  T3: 清理旧格式数据

离线迁移(Offline Migration)—— 大数据量:

适用:
├── 数据量 > 1000 万行
├── 变更类型为破坏性变更
└── 需要精确验证

实现方式:
  1. 快照(Snapshot)
     对当前数据创建快照
  2. 转换(Transform)
     在快照上执行数据转换
  3. 验证(Verify)
     对比转换前后的数据
  4. 原子切换(Atomic Switch)
     一次性切换到新数据

#4.2 类型转换规则

Code
安全类型转换(自动执行):

源类型      → 目标类型      转换规则
BOOLEAN     → STRING       "true" / "false"
INT         → LONG         直接扩展
INT         → DOUBLE       直接扩展
LONG        → DOUBLE       直接扩展(注意精度)
FLOAT       → DOUBLE       直接扩展
STRING      → TEXT         直接扩展
DATE        → TIMESTAMP    追加 "T00:00:00Z"
ENUM        → STRING       使用枚举值的字符串表示

需要验证的类型转换(逐行检查):

源类型      → 目标类型      验证规则
STRING      → INT          每行必须是合法整数
STRING      → DOUBLE       每行必须是合法数字
STRING      → DATE         每行必须匹配日期格式
STRING      → ENUM         每行必须在枚举值列表中
DOUBLE      → INT          每行的小数部分必须为 0
TIMESTAMP   → DATE         丢弃时间部分(需确认)

验证报告示例:
┌────────────────────────────────────────────────┐
│  Type Conversion Validation                     │
│  STRING → INT for "Order.legacyAmount"          │
├────────────────────────────────────────────────┤
│  Total rows: 1,000,000                          │
│  Valid rows: 999,847                            │
│  Invalid rows: 153                              │
│                                                 │
│  Sample invalid values:                         │
│  Row 12345: "N/A"                               │
│  Row 67890: "123.45"                            │
│  Row 99001: ""                                  │
│  Row 99502: "unknown"                           │
│                                                 │
│  Action required:                               │
│  ├── Fix invalid data before migration          │
│  ├── Or provide a fallback value                │
│  └── Or change target type to allow nulls       │
└────────────────────────────────────────────────┘

#4.3 迁移脚本自动生成

Code
coomia-dip 自动生成迁移脚本:

示例:Order v3 → v4 的迁移脚本

-- Migration: Order v3 → v4
-- Generated: 2025-01-15T10:05:00Z
-- Author: system (auto-generated)

-- Step 1: Add new column "priority"
ALTER TABLE order_objects
  ADD COLUMN priority VARCHAR(20) DEFAULT 'MEDIUM' NOT NULL;

-- Step 2: Rename column "amount" to "totalAmount"
ALTER TABLE order_objects
  RENAME COLUMN amount TO total_amount;

-- Step 3: Change type DOUBLE → DECIMAL
ALTER TABLE order_objects
  ALTER COLUMN total_amount TYPE DECIMAL(18,4)
  USING CAST(total_amount AS DECIMAL(18,4));

-- Step 4: Update indexes
CREATE INDEX CONCURRENTLY idx_order_priority
  ON order_objects(priority);

-- Step 5: Add deprecation marker for "legacyCode"
COMMENT ON COLUMN order_objects.legacy_code IS
  '@Deprecated: Use orderCode instead. Removal in v5.';

-- Rollback script (auto-generated):
-- ALTER TABLE order_objects DROP COLUMN priority;
-- ALTER TABLE order_objects RENAME COLUMN total_amount TO amount;
-- ALTER TABLE order_objects ALTER COLUMN amount TYPE DOUBLE PRECISION;
-- DROP INDEX idx_order_priority;

每个迁移脚本都有对应的回滚脚本
回滚脚本在 Apply 之前自动验证可执行性

#5. 版本兼容性管理

#5.1 API 兼容层

Code
Schema 变更后,API 需要支持多版本消费者:

场景:Order.amount 重命名为 Order.totalAmount

API 兼容策略:

v3 消费者(旧)请求 /api/v1/orders/123:
{
  "orderId": "123",
  "amount": 610.13,        ← 旧名字仍然可用
  "status": "ACTIVE"
}

v4 消费者(新)请求 /api/v1/orders/123:
{
  "orderId": "123",
  "totalAmount": 610.13,   ← 新名字
  "priority": "HIGH",      ← 新属性
  "status": "ACTIVE"
}

不指定版本的消费者 —— 返回最新版本

指定版本的消费者:
  GET /api/v1/orders/123?schemaVersion=v3
  → 返回 v3 格式(使用属性别名映射)

兼容期策略:
  v3 别名有效期:v4 发布后 30 天
  30 天后:使用旧名字会返回 Deprecation Warning Header
  60 天后:使用旧名字会返回 400 Bad Request

#5.2 SDK 自动更新

Code
Schema 变更后,SDK 自动生成新的类型定义:

Python SDK 变更:

# v3 (旧版本)
class Order(OntologyObject):
    order_id: str
    amount: float
    status: str

# v4 (新版本 - 自动生成)
class Order(OntologyObject):
    order_id: str
    total_amount: Decimal        # 重命名 + 类型变更
    priority: OrderPriority      # 新属性
    status: str
    legacy_code: str | None      # 标记为 @deprecated

    @deprecated("Use 'total_amount' instead")
    @property
    def amount(self) -> Decimal:
        return self.total_amount

SDK 版本管理:
  SDK 1.0.x → Order v3
  SDK 1.1.x → Order v4(向后兼容 v3 别名)
  SDK 2.0.x → Order v4(移除 v3 别名)

#5.3 消费者通知机制

Code
Schema 变更通知流:

变更提出 → 验证通过 → 发送通知

通知渠道:
├── Webhook —— 推送到注册的消费者端点
├── Event Bus —— 发布到 Kafka topic "schema-changes"
├── Email —— 发送给受影响 ObjectType 的 owner
└── Dashboard —— 在管理控制台显示变更提醒

通知内容:
{
  "eventType": "SCHEMA_CHANGE_PROPOSED",
  "objectType": "Order",
  "version": {"from": "v3", "to": "v4"},
  "changes": [
    {"type": "ADD_PROPERTY", "property": "priority"},
    {"type": "RENAME_PROPERTY", "from": "amount", "to": "totalAmount"},
    {"type": "TYPE_CHANGE", "property": "totalAmount",
     "from": "DOUBLE", "to": "DECIMAL"}
  ],
  "impact": {
    "derivedProperties": 3,
    "actionTypes": 2,
    "externalConsumers": 5
  },
  "compatibilityPeriod": "30 days",
  "proposedApplyDate": "2025-01-20T00:00:00Z"
}

消费者响应:
├── ACK —— 已知悉,会在兼容期内更新
├── NACK —— 反对变更,需要更多时间
├── REQUEST_EXTENSION —— 请求延长兼容期
└── 超时未响应 —— 系统发送提醒

#6. 版本回滚

#6.1 自动回滚触发条件

Code
以下情况自动触发回滚:

  1. 数据迁移步骤失败
     → 已执行的步骤全部回滚
     → 恢复到变更前的 Schema 版本

  2. 迁移后验证失败
     → 数据完整性检查不通过
     → 回滚数据和 Schema

  3. 手动触发回滚
     → 管理员发现问题
     → API: POST /api/v1/ontology/schema/rollback/{version}

回滚限制:
├── 只能回滚到上一个版本(v4 → v3)
├── 如果新数据使用了新 Schema 特有的值,回滚需要处理
│   例:v4 新增了 priority 属性,已有 1000 条记录设置了值
│   回滚时:这 1000 条记录的 priority 值会丢失
│   → 系统会警告并要求显式确认
└── 超过回滚保护期(默认 7 天)后,旧版本数据可能已清理

#6.2 回滚策略

Code
回滚执行步骤:

Step 1: 停止新版本的数据写入
  ├── API 层切换回旧版本 Schema
  └── 新数据按旧 Schema 写入

Step 2: 数据回迁
  ├── 执行自动生成的回滚脚本
  ├── 验证回迁数据的完整性
  └── 处理新数据中旧 Schema 无法表达的值

Step 3: 恢复依赖项
  ├── 派生属性表达式恢复
  ├── ActionType 参数恢复
  ├── API 别名移除
  └── SDK 回退通知

Step 4: 验证
  ├── 数据完整性检查
  ├── 派生属性重算
  ├── API 端到端测试
  └── 消费者健康检查

Step 5: 发布回滚通知
  ├── 通知所有消费者 Schema 已回滚
  └── 记录回滚原因到审计日志

#7. Schema 变更的最佳实践

#7.1 渐进式变更

Code
推荐的变更模式——分步骤而非一步到位:

不推荐:直接重命名并改类型
  v3 → v4: amount(DOUBLE) → totalAmount(DECIMAL)
  风险:一次变更包含两个破坏性操作

推荐:分两次变更
  v3 → v4: 添加 totalAmount(DECIMAL),保留 amount
            后台同步 amount → totalAmount
  v4 → v5: 标记 amount 为 @Deprecated
  v5 → v6: 确认无消费者使用 amount 后删除

每次变更只做一件事:
├── 要么新增
├── 要么重命名
├── 要么改类型
├── 要么删除
└── 不要混合多个破坏性操作

#7.2 Schema 变更日志

Code
每次 Schema 变更都记录到审计日志:

SchemaChangeLog:
├── changeId: "sch-20250115-001"
├── objectTypeId: "Order"
├── versionFrom: "v3"
├── versionTo: "v4"
├── changes: [...]
├── author: "alice@company.com"
├── approver: "bob@company.com"
├── proposedAt: "2025-01-15T10:00:00Z"
├── validatedAt: "2025-01-15T10:05:00Z"
├── appliedAt: "2025-01-15T10:10:00Z"
├── migrationDurationMs: 35000
├── affectedRecords: 1000000
├── status: "COMPLETED"
└── rollbackAvailableUntil: "2025-01-22T10:10:00Z"

查询历史:
  GET /api/v1/ontology/schema/changelog?objectTypeId=Order

用途:
├── 审计:谁在什么时候改了什么
├── 调试:某个时间点的 Schema 长什么样
├── 合规:变更经过谁的审批
└── 回溯:追踪数据问题到 Schema 变更

#7.3 Schema 设计原则

Code
设计 Schema 时考虑未来变更的原则:

原则 1:宽松设计
  ├── 字符串字段用 TEXT 而非 VARCHAR(50)
  ├── 数值字段用 DECIMAL 而非 INT
  ├── 日期字段用 TIMESTAMP 而非 DATE
  └── 预留扩展字段(metadata: JSON)

原则 2:语义命名
  ├── 使用业务语义命名:totalAmount 而非 amt
  ├── 使用一致的命名模式:createdAt, updatedAt, deletedAt
  └── 避免缩写:使用 description 而非 desc

原则 3:版本感知
  ├── API 总是标注版本号
  ├── 消费者声明自己支持的版本范围
  ├── Schema 变更有充足的兼容期
  └── 自动化测试覆盖多版本兼容性

原则 4:可逆性
  ├── 每次变更都有回滚方案
  ├── 删除前先 Deprecate
  ├── 类型变更优先使用安全扩展方向
  └── 保留变更前的数据快照

#8. 自动化 Schema 演进

#8.1 Schema 变更检测

Code
coomia-dip 可以从数据变化中推断 Schema 变更需求:

场景:数据写入时发现不匹配

消息队列收到的数据:
{
  "orderId": "ORD-2025-001",
  "totalAmount": 610.13,
  "priority": "HIGH",        ← Schema 中没有这个属性!
  "customerSegment": "VIP"   ← Schema 中没有这个属性!
}

自动推断流程:
  1. 检测到未知属性 "priority" 和 "customerSegment"
  2. 分析最近 100 条数据,确认这不是偶发的脏数据
     priority 出现 98/100 次,值域 = {LOW, MEDIUM, HIGH, URGENT}
     customerSegment 出现 95/100 次,值域 = {VIP, REGULAR, NEW}
  3. 自动生成 Schema 变更提案:
     ADD_PROPERTY priority: ENUM(LOW, MEDIUM, HIGH, URGENT)
     ADD_PROPERTY customerSegment: ENUM(VIP, REGULAR, NEW)
  4. 发送通知给 ObjectType owner 审批
  5. 审批通过后自动应用

注意:自动推断只能建议新增属性
类型变更和删除必须人工发起

#8.2 Schema 差异比较

Code
API: GET /api/v1/ontology/schema/diff?from=v3&to=v4&objectTypeId=Order

响应:
{
  "objectTypeId": "Order",
  "from": "v3",
  "to": "v4",
  "diff": {
    "added": [
      {"name": "priority", "type": "ENUM", "values": ["LOW","MEDIUM","HIGH","URGENT"]}
    ],
    "modified": [
      {
        "name": "amount → totalAmount",
        "changes": {
          "name": {"from": "amount", "to": "totalAmount"},
          "type": {"from": "DOUBLE", "to": "DECIMAL(18,4)"}
        }
      }
    ],
    "deprecated": [
      {"name": "legacyCode", "message": "Use orderCode instead", "removalVersion": "v5"}
    ],
    "removed": []
  },
  "compatibility": "BACKWARD_COMPATIBLE_WITH_MIGRATION",
  "migrationRequired": true
}

#9. 多环境 Schema 同步

#9.1 环境晋升流程

Code
Schema 变更在多环境间的晋升:

开发环境 (dev) → 测试环境 (staging) → 生产环境 (prod)

流程:
  1. dev 环境:自由变更,无需审批
  2. staging 环境:从 dev 同步 Schema,运行集成测试
  3. prod 环境:从 staging 晋升,需要审批

Schema 同步 API:
  POST /api/v1/ontology/schema/promote
  {
    "objectTypeId": "Order",
    "fromEnvironment": "staging",
    "toEnvironment": "prod",
    "version": "v4"
  }

晋升前检查:
├── staging 环境的集成测试是否全部通过
├── 数据迁移是否在 staging 验证过
├── 消费者兼容性测试是否通过
├── 回滚方案是否就绪
└── 审批人是否已批准

#9.2 Schema 锁定

Code
关键 ObjectType 的 Schema 锁定机制:

锁定级别:
  UNLOCKED    —— 任何人都可以变更
  SOFT_LOCK   —— 变更需要 owner 审批
  HARD_LOCK   —— 变更需要多人审批(2/3 审批者同意)
  FROZEN      —— 禁止任何变更(除非解冻)

设置锁定:
  PUT /api/v1/ontology/schema/lock
  {
    "objectTypeId": "Order",
    "lockLevel": "HARD_LOCK",
    "approvers": ["alice", "bob", "carol"],
    "reason": "Production release freeze"
  }

用途:
├── 发布冻结期:FROZEN
├── 核心模型保护:HARD_LOCK
├── 日常管控:SOFT_LOCK
└── 实验性模型:UNLOCKED

#10. Schema 变更与派生属性 DAG 的联动

Code
Schema 变更会影响 DAG:

场景:重命名 Order.amount → Order.totalAmount

DAG 影响分析:
  1. 扫描所有引用 "Order.amount" 的 DAG 节点
  2. 找到:
     Customer.lifetimeValue 的表达式中引用了 Order.amount
     Order.profit 的表达式中引用了 amount
  3. 自动更新:
     Customer.lifetimeValue: "SUM(Order.amount)" → "SUM(Order.totalAmount)"
     Order.profit: "amount - cost" → "totalAmount - cost"
  4. 重新验证 DAG:
     表达式语法检查 ✓
     环检测 ✓
     类型兼容检查 ✓

场景:删除 Order.legacyField

DAG 影响分析:
  1. 扫描所有引用 "Order.legacyField" 的 DAG 节点
  2. 找到:Order.legacyScore = legacyField * 0.5
  3. 结论:不能删除!legacyField 被派生属性依赖
  4. 建议:先删除 Order.legacyScore,再删除 Order.legacyField

操作顺序:
  Step 1: 删除派生属性 Order.legacyScore
  Step 2: 从 DAG 中移除 legacyScore 节点
  Step 3: 删除源属性 Order.legacyField
  Step 4: 重新验证 DAG 完整性

#Key Takeaways

  1. Schema 变更是 Ontology 演进的常态——coomia-dip 通过版本化、兼容性检查、自动迁移将 Schema 变更从"高风险操作"变成了"可控的日常流程",消灭了"改 Schema 就爆炸"的恐惧。
  2. 三阶段协议是安全阀——Propose(记录意图)→ Validate(自动检查)→ Apply(受控执行),每个阶段都可以中断和回滚,确保不会出现"改到一半系统不可用"的情况。
  3. 兼容性规则引擎自动分类——安全变更自动通过,危险变更要求确认,禁止的变更直接拒绝,不依赖人的判断来保证安全。
  4. API 和 SDK 的多版本支持——通过属性别名和版本协商,新旧消费者可以和平共存,给下游充足的迁移时间。
  5. Schema 变更与 DAG 联动——重命名属性时自动更新派生属性表达式,删除属性时先检查 DAG 依赖,确保 Schema 变更不会静默打破计算链。

#Next Article

下一篇 S4-10 Metrics 作为 Ontology 将讨论如何将监控指标和业务 KPI 统一纳入 Ontology 模型——不再把 metrics 当作独立系统,而是让它成为 ObjectType 的一部分,实现"业务数据"和"运营指标"的统一查询和联动分析。

#ontology #schema-evolution #version-control #migration #backward-compatibility #breaking-change #data-migration #api-versioning