能控性 = 业务状态能否被干预
解决什么问题:业务建模的第一步——判断哪些业务状态是可以被干预的(能控),哪些是不能被干预的(不能控)。 不能控的业务状态不能作为决策依据,必须通过能控部分间接观测。
业务问题
真实场景
场景 1:误把"展示"当成"控制"
需求:用户希望"看到自己最近 7 天的浏览记录"
设计:在用户界面展示最近 7 天的浏览历史
问题:
- 展示 ≠ 控制
- 用户能看到(受控量),但不能修改(控制量)
- 如果需求变成"删除浏览记录",怎么办?
→ "展示"是"观测","删除"是"控制"
→ 业务建模必须先识别"控制"边界场景 2:把"间接状态"当成直接状态
需求:业务希望"提升用户活跃度"
问题:
- "活跃度"是什么?
- 7 天内登录次数?
- 30 天内下单次数?
- 平均使用时长?
- 业务方说"活跃度",但没人能定义清楚
- 工程师做了一个"活跃度"指标,但和业务理解不一致
→ "活跃度"是不能控的(不能直接干预"活跃度"本身)
→ 能控的是:推送、优惠、内容(这些可以影响活跃度)
→ 必须通过"控制动作"间接影响"活跃度"场景 3:把"观测"当成"干预"
需求:业务希望"提升订单转化率"
设计:
- 工程师做了一个"转化率"仪表盘
- 业务方问"转化率怎么提升?"
- 工程师:"转化率只是统计指标,影响它的因素太多了"
→ "转化率"是"受控量"(只能观测)
→ 工程师错误地把"观测"当成"干预"路径
→ 实际上"转化率"是被多种因素影响的,不能直接干预失败的典型做法
做法 1:把所有状态都当成能控
✗ 业务说"提升活跃度" → 工程师直接做"活跃度"接口
✗ 业务说"提升转化率" → 工程师直接做"转化率"接口
后果:
- 做出"伪控制"接口
- 调用这些接口不能真的改变业务做法 2:忽略"不能控"状态
✗ 只关心能控的,忽略不能控的
✗ "活跃度"这种不能控的,不在系统里
后果:
- 业务方想看"活跃度"曲线 → 没有
- 业务方和工程师沟通错位做法 3:把"不能控"当成"能控"的接口
✗ 业务方说"活跃度太低"
✗ 工程师设计"修改活跃度"接口
后果:
- 接口做出来但不能真的修改活跃度
- 调用了也没用
- 业务方骂"系统没用"控制论视角
数学原型
钱学森《工程控制论》Ch 4 §4.11:
"一个线性系统总可以分解成四个部分:能控能观测 / 能控不能观测 / 能观测不能控 / 不能控不能观测。在这四部分中,传递函数只能反映完全能控、完全能观测部分。"
钱学森 §5.5:
"状态反馈可以任意配置闭环极点,前提是系统能控。(即:通过选择反馈增益 K,使得闭环极点位于任意指定位置)如果系统不能控,则不能通过状态反馈任意配置闭环极点。"
控制论解法
系统能控性分解:
┌─────────────────┬─────────────────┐
│ 能控能观测 │ 能控不能观测 │
│ (理想子系统) │ (黑盒子系统) │
├─────────────────┼─────────────────┤
│ 能观测不能控 │ 不能控不能观测 │
│ (外部信号) │ (扰动 / 噪声) │
└─────────────────┴─────────────────┘
业务建模任务:
- 识别哪些业务状态在哪个子集
- 能控能观测 → 重点支持
- 能控不能观测 → 通过能观测量推断
- 能观测不能控 → 通过控制动作间接影响
- 不能控不能观测 → 不作为决策依据与业务的对应
| 控制论概念 | 软件工程 |
|---|---|
| 能控 | 业务状态可被接口 / 命令改变 |
| 能观测 | 业务状态可被监控 / 日志读出 |
| 状态反馈 | 通过控制接口改变状态 |
| 不能控的状态 | 外部因素 / 业务方决策 / 物理世界 |
| 不能观测的状态 | 内部黑盒 / 第三方系统 |
核心概念
定义
能控性:在有限时间内,通过施加控制作用,能否将系统从任意初始状态转移到任意目标状态。
业务对位:业务状态能否被系统的接口 / 命令改变。
系统的四类状态
| 类型 | 控制性 | 可观测性 | 业务对位 | 处理方式 |
|---|---|---|---|---|
| 能控能观测 | ✅ | ✅ | 业务状态 + 控制接口 + 监控 | 重点支持 |
| 能控不能观测 | ✅ | ❌ | 控制接口存在但没有监控 | 加监控 |
| 能观测不能控 | ❌ | ✅ | 业务状态可见但无控制接口 | 通过控制动作间接影响 |
| 不能控不能观测 | ❌ | ❌ | 外部因素 / 内部黑盒 | 不作为决策依据 |
关键属性
| 属性 | 含义 |
|---|---|
| 控制的相对性 | 能控是相对控制量而言——只有对控制量才有自由去改变 |
| 观测的相对性 | 能观测是相对观测通道而言——只有能观测才能作为反馈 |
| 不能控不等于不重要 | 不能控的状态可能很重要(如活跃度),但只能间接影响 |
| 不能控的稳定性 | 不能控的状态不能作为决策依据,只能作为监控指标 |
能控性的 4 步法
步骤 1:识别业务状态
├─ 列出所有业务实体及其状态
└─ 输出:业务状态清单
步骤 2:识别业务动作
├─ 列出所有可以改变业务状态的动作
└─ 输出:业务动作清单
步骤 3:判断能控性
├─ 每个状态变化:是否有对应的业务动作?
└─ 输出:能控性矩阵
步骤 4:设计控制接口
├─ 能控的状态 → 提供控制接口
└─ 不能控的状态 → 通过能控部分间接观测工程实践
步骤 1:识别业务状态
业务状态清单(以电商订单为例):
| 状态 | 描述 | 是否有控制接口 |
|---|---|---|
| 待支付 | 订单创建后未支付 | ✅ 创建/取消 |
| 已支付 | 支付成功 | ✅ 触发支付 |
| 已发货 | 商品已发出 | ✅ 触发发货 |
| 已完成 | 用户确认收货 | ✅ 触发确认 |
| 已取消 | 订单被取消 | ✅ 触发取消 |
| 已退款 | 支付后退款 | ✅ 触发退款 |步骤 2:识别业务动作
业务动作清单(控制量):
- 创建订单:待支付 ← 无
- 支付订单:待支付 → 已支付
- 取消订单:待支付 → 已取消
- 发货:已支付 → 已发货
- 确认收货:已发货 → 已完成
- 申请退款:已支付 → 退款中
- 退款完成:退款中 → 已退款步骤 3:判断能控性矩阵
能控性矩阵:
| 状态 \ 动作 | 创建 | 支付 | 取消 | 发货 | 收货 | 退款 |
|---|---|---|---|---|---|---|
| 待支付 | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ |
| 已支付 | ✗ | ✗ | ✗ | ✓ | ✗ | ✓ |
| 已发货 | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ |
| 已完成 | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
| 已取消 | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
| 已退款 | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
判断:
- 任何状态都至少有一个控制动作 → 完全能控
- 终态(已完成/已取消/已退款)没有后续控制 → 设计正确步骤 4:设计控制接口
控制接口设计:
1. 显式控制接口
- 创建订单(POST /orders)
- 支付订单(POST /orders/{id}/pay)
- 取消订单(POST /orders/{id}/cancel)
- 发货(POST /orders/{id}/ship)
- 收货(POST /orders/{id}/confirm)
- 退款(POST /orders/{id}/refund)
2. 状态机(聚合根)
- 每个状态都有合法的转换路径
- 不能控的状态 = 终态
3. 不能控的状态处理
- 用户活跃度(不能控) → 通过推送、优惠(能控)间接影响
- 业务转化率(不能控) → 通过 A/B 实验(能控)间接测量不能控状态的 3 种处理方式
方式 1:通过能控部分间接观测
例:订单的"用户满意度"不能直接控制
但可以通过:
- 评价(能控)
- 退货(能控)
- 复购率(能控)
→ "用户满意度"通过这些能控指标间接观测方式 2:作为控制目标
例:业务希望"用户活跃度↑"
但活跃度不能直接控制
→ 设计控制动作(推送 / 优惠 / 内容)
通过这些能控动作影响活跃度方式 3:接受不能控,纳入监控
例:股票价格、市场行情
不能控 → 不作为决策依据
→ 只监控,不控制常见反模式
反模式 1:状态可见但不能控
症状:
- 系统展示某个状态
- 但没有接口改变这个状态
- 例:用户积分显示但不能修改
后果:状态是"虚假的",业务方误以为可控
正解:不展示不能控的状态,或明确标注"只读"反模式 2:控制接口混乱
症状:
- 同一个状态有多种改变方式
- 不同接口的行为不一致
- 例:订单状态可被多个服务修改
后果:状态不可预测,调试困难
正解:单一控制接口(聚合根),明确的状态机反模式 3:忽略不能控状态
症状:
- 业务中存在不能控的状态
- 但被当作能控状态处理
后果:业务规则错误,边界判断失误
正解:显式识别不能控状态,明确处理方式反模式 4:把不能控作为"目标"
症状:
- "提升活跃度" → 工程师直接做"活跃度"接口
- 接口做出来但不能真的修改活跃度
后果:接口没用,业务方骂"系统没用"
正解:识别控制量(推送/优惠/内容),活跃度是结果工程实例:电商订单的能控性分析
业务状态:
- 待支付
- 已支付
- 已发货
- 已完成
- 已取消
- 退款中
- 已退款
业务动作(控制量):
- 创建订单(→ 待支付)
- 支付订单(待支付 → 已支付)
- 取消订单(待支付 → 已取消)
- 申请退款(已支付 → 退款中)
- 退款完成(退款中 → 已退款)
- 发货(已支付 → 已发货)
- 确认收货(已发货 → 已完成)
能控性判断:
- 待支付 → 已支付:是(支付)
- 待支付 → 已发货:否(必须先支付)
- 已支付 → 已发货:是(发货)
- 已完成 → 其他:否(终态)
- 任何 → 已取消:部分是(看业务规则)
→ 控制接口设计:
- 订单服务暴露:创建 / 支付 / 发货 / 收货 / 取消 / 退款
- 不暴露:强制修改状态(除非管理员)工程实例:推荐系统的能控性
业务目标:提升用户活跃度
问题:活跃度是结果指标,不能直接控制
控制量(能控的):
- 推荐内容
- 推送时机
- 推送文案
- 优惠力度
- 推送频次
被控量(能观测但不能直接控制):
- 用户活跃度
- 点击率
- 转化率
- 留存率
设计:
1. 推荐系统暴露推荐接口(控制)
2. 通过 A/B 实验(能控)测量活跃度变化(能观测)
3. 通过反馈调节(控制动作的调整)影响活跃度
→ "活跃度"是不能控的,但通过"推荐"+"实验"+"反馈"三个能控的环节间接影响性能指标
能控性健康指标
| 指标 | 设计目标 | 衡量方法 |
|---|---|---|
| 状态可覆盖率 | 100% 业务状态有控制接口 | 控制接口数 / 业务状态数 |
| 状态可观测率 | 100% 业务状态有监控 | 监控埋点数 / 业务状态数 |
| 不能控状态识别率 | 100% | 显式标注不能控的状态 / 总不能控状态 |
| 控制接口一致性 | 100% | 单一控制接口的状态 / 总状态 |
| 状态变更失败率 | < 0.1% | 状态变更失败次数 / 总变更次数 |
监控设计
关键监控:
- 每个业务状态的控制接口调用次数(业务正常 = 高)
- 状态变更失败率(业务集成健康)
- 不能控状态的指标变化(业务趋势)
告警分级:
- P1:状态变更失败率 > 1%
- P2:不能控状态指标异常变化边界判断
何时不用
场景 1:业务极简
- 业务实体 < 5 个
- 业务状态 < 10 个
- 业务规则简单
→ 不需要复杂的能控性分析,直接建模即可场景 2:业务完全确定
- 所有路径都已建模
- 所有状态都可控
- 没有不能控的状态
→ 不需要"能控性"分析,因为所有都是能控的场景 3:业务完全不能控
- 业务受外部因素主导
- 系统只是"观测"工具
- 不做任何干预
→ 不需要"能控性"分析,只需要"观测"能力与其他方法论的边界
| 相邻概念 | 区别 |
|---|---|
| 模型与现实 | 模型与现实关注"模型是否反映现实";本方法论关注"业务状态能否被干预" |
| 线性边界 | 线性边界关注"简化假设何时失效";本方法论关注"业务状态的控制能力" |
| 限界上下文 | 限界上下文关注"系统边界";本方法论关注"状态的控制能力" |
| 状态机 | 状态机是实现;本方法论是判断标准 |
何时升级(更精细的能控性分析)
当以下情况出现时,考虑更精细的能控性分析:
1. 业务状态数 > 50 → 引入状态机 + 显式能控性矩阵
2. 业务方对"控制"边界频繁争论 → 显式标注
3. 不能控的状态被误用为决策依据 → 重新建模
4. 多团队协作出现状态冲突 → 显式划分能控性关键洞察
钱学森 §1.5 原话:"只有对控制量我们才有自由去改变它。"
业务建模的核心任务就是:
- 识别哪些业务状态是"控制量"(可以改变)
- 识别哪些业务状态是"受控量"(只能被前者影响)
- 识别哪些业务状态是"外部因素"(不能控)
不能控的状态不能作为决策依据——只能作为监控指标或间接观测。
工程师的常见错误:把所有状态都当成能控的,做出"伪控制"接口。
相关链接
IDDD 对位
- 能控性 → BC 可独立演进 → Module vs BC(Ch 9)
- 能观性 → 状态可查询 → 聚合 4 规则(Ch 10)
核心:BC 必须既能控(独立演进)又能观(状态可查)——否则不能称为 BC,只是模块。