Skip to content

业务约束跨层传播:一个数字到底要改几层

内容类型:专项方法难度:进阶适合:修改阈值、上下限、时间、频率、单位、次数、枚举范围等现有业务规则阅读时间:约 9 分钟

“把 15 A 改成 16 A”“把 10 分钟改成 600 秒”“频率上限从 1000 Hz 改到 1500 Hz”看起来都只是改一个数字。

但在实际上位机项目里,同一个规则可能同时存在于:

text
界面提示
输入校验
业务模型
MES / API
设备下发
配置持久化
历史数据显示
测试

如果只改其中一层,软件可能出现“界面允许、后端拒绝”“MES 还是旧值”“设备实际没收到新范围”这类很难第一眼发现的问题。

反过来,如果每改一个数字都全仓库搜索、重构所有相似常量,又会把一个 10 分钟任务做成半天。

V10 的办法是先判断:这是 DisplayOnly,还是 DomainConstraint?

1. 第一道判断:DisplayOnly 还是 DomainConstraint

DisplayOnly

只改变展示,不改变系统接受、判断、发送或保存的业务规则。

例如:

  • “最大电流”改成“充电电流上限”;
  • 15.0 A 改成 15 A 的显示格式;
  • 单位标签移到数值右侧;
  • 页面说明文字更新;
  • 曲线纵轴视觉范围调整,但真实校验和设备范围不变。

默认:

text
Fast / MicroPatch
→ 找直接展示位置
→ 最小修改
→ V0 / V1

不要自动扩到 Domain、MES、设备和测试。

DomainConstraint

这个值决定系统允许什么、拒绝什么、何时触发什么行为、向外部系统传什么、向设备实际下发什么

例如:

  • 电压最大允许值;
  • 频率合法范围;
  • 测试时长上下限;
  • 连续 5 次才报警;
  • 温度超过 80 ℃触发联锁;
  • 参数写 MES 时的单位和范围;
  • 设备下发值的倍率和边界;
  • 一次测试最大循环次数。

这时走固定传播链,而不是只改 UI。

2. 固定传播链:只沿 5 个层级检查一次

V10 推荐:

text
UI

Domain / Application

MES / API

DeviceApply

FocusedTests

这是一条检查链,不是说所有项目一定都有五层,也不是要求每次五层都修改。

原则是:

每层检查一次;确认没有这个约束就停止该分支,不做全仓库无限搜索。

3. 第 1 层:UI 到底只是显示,还是也做输入约束

检查:

  • Minimum / Maximum
  • ValidationRule;
  • ViewModel 输入校验;
  • Slider / NumericUpDown 范围;
  • ComboBox 可选项;
  • 提示文字;
  • 单位标签;
  • 启用/禁用条件。

典型错误:

text
界面提示:允许 0–16 A
实际输入校验:仍然 <= 15 A

或者反过来:

text
UI 可以输入 16 A
后端点击“应用”时又报超范围

UI 是入口证据,但通常不应该成为唯一业务约束来源。

4. 第 2 层:Domain / Application 才是正式规则吗

重点找:

  • 参数模型验证;
  • Application Service;
  • workflow 前置条件;
  • 状态机条件;
  • 共享常量;
  • 配置 schema;
  • 业务对象构造校验。

需要回答:

text
真正决定“这个值是否合法”的 owner 是谁?

如果项目已经有稳定 Domain/Application owner,优先修改 owner,再让 UI 和外部边界复用它。

不要为了这次小改临时再造第二套规则来源。

5. 第 3 层:MES / API / 外部接口有没有自己的范围或单位

上位机常见问题:本地已经改了,但接口契约还是旧语义。

检查:

  • DTO 字段单位;
  • API validation;
  • MES 上传/下发范围;
  • JSON/XML 字段;
  • 数据库接口;
  • 外部系统文档;
  • 兼容旧版本的转换逻辑。

例如:

text
本地内部:duration = 600 秒
MES 字段:durationMinutes = 10

如果只是内部实现从“分钟”改成“秒”,并不代表 MES 契约也应该跟着改。

这里必须区分:

text
InternalRepresentation
ExternalContract

不要因为内部重构顺手破坏外部协议。

6. 第 4 层:DeviceApply 才决定真实设备收到什么

涉及设备参数时,最终还要看:

  • 下发命令;
  • SCPI / Modbus / CAN / 自定义协议转换;
  • 倍率、偏移、单位;
  • clamp / round;
  • 设备自身范围;
  • apply 前二次校验;
  • 通道映射。

例如:

text
UI 输入:16.0 A
Domain:16.0 A
Device protocol:单位为 0.1 A
实际下发:160

如果只改了界面和 Domain,却仍在 DeviceApply 中 clamp 到 150,用户会看到“参数保存成功”,但设备实际只收到 15 A。

设备真实写操作是否安全,仍需遵守人工确认边界。

7. 第 5 层:FocusedTests 只保护这次真正改变的语义

DomainConstraint 很适合 focused test,因为它通常有清晰边界。

例如上限从 15 A 改到 16 A:

text
15.999 A → Accepted
16.000 A → Accepted
16.001 A → Rejected

如果有设备编码:

text
16.0 A → DeviceRaw 160

如果有外部契约:

text
External DTO 保持原单位/字段语义

不需要因为改一个阈值自动跑整个测试仓库。

8. 一个实战例子:10 分钟改成 600 秒

这类任务最危险的地方不是数值本身,而是语义层变化

先画最短链:

text
UI 输入
→ ViewModel
→ Domain/Application
→ 配置持久化
→ MES/API
→ Timer / Workflow
→ 历史显示

然后逐层问:

原语义新语义是否应该改
UI用户输入分钟用户仍输入分钟
内部模型minutesseconds
配置文件历史字段是 minutes兼容旧配置视兼容策略
MESminutes外部契约仍 minutes
workflowTimeSpan.FromMinutesseconds 表示
历史显示显示分钟仍显示分钟

这说明“底层改秒”不等于“全系统字段都改秒”。

真正需要传播的是业务含义,不是机械改变量名和数字。

9. 一个实战例子:频率上限 1000 Hz → 1500 Hz

先判断用户到底说的是:

正式规格修改

text
以后系统正式支持到 1500 Hz

→ DomainConstraint。

检查:

text
UI range
→ Domain validation
→ MES/API
→ Device capability / apply
→ focused boundary tests

临时边界实验

text
先放开到 1500 Hz,我测一下

→ ExperimentalChange。

优先:

text
engineering override
→ 窄入口
→ 正式 Domain/MES 不变
→ 测试结束恢复

不要把实验条件误升级为正式产品规格。

10. 一个数字出现很多次,不能靠“全部替换”解决

全局搜索看到:

text
1000
1000
1000
1000

不能直接全部替换成 1500

这些数字可能分别表示:

  • 频率上限 1000 Hz;
  • 超时 1000 ms;
  • 缓冲区 1000 点;
  • 测试样本 1000 次。

正确做法:

text
先找业务 owner
→ 再看 direct consumers
→ 每层确认语义
→ 只改同一个 constraint 的传播点

11. 配置项是不是 DomainConstraint

配置文件里有一个数值,不代表它天然是正式业务规则。

可以分:

text
UserPreference
DisplaySetting
EngineeringOverride
DomainConstraint
DeviceCapability
ExternalContract

例如:

  • 曲线刷新 200 ms:UI/性能设置;
  • 最大高压 3000 V:可能是 DomainConstraint + DeviceCapability;
  • 调试模式允许 3300 V:ExperimentalChange / EngineeringOverride;
  • MES 字段单位:ExternalContract。

分类以后才知道改动应该传播到哪里。

12. 新旧数据和历史兼容不要遗漏

如果 DomainConstraint 进入配置、数据库、历史结果,需要额外问:

  • 旧配置还能不能读;
  • 旧值是否需要迁移;
  • 新版打开旧测试记录时单位是否正确;
  • 序列化字段是否保持兼容;
  • 导出文件是否改变含义。

这时验证通常至少 V2。

但是如果约束从未持久化,就不要为了“保险”自动做数据库迁移设计。

13. 最小检查模板

yaml
Constraint:
BusinessMeaning:
Change:

Classification: DisplayOnly | DomainConstraint | ExperimentalChange

UI:
  Present: Yes/No
  ChangeNeeded:

DomainApplication:
  Owner:
  ChangeNeeded:

MES_API:
  Contract:
  ChangeNeeded:

DeviceApply:
  ConversionOrClamp:
  ChangeNeeded:

PersistenceCompatibility:
  Relevant: Yes/No
  Action:

FocusedVerification:
  - boundary case
  - conversion case
  - compatibility case if relevant

NonGoals:

14. 可直接复制的 Prompt

text
$host-computer-dev
我要修改一个现有上位机参数/阈值/范围。

当前规则:
【填写】

目标规则:
【填写】

请先判断这是 DisplayOnly、DomainConstraint 还是 ExperimentalChange。

如果是 DisplayOnly,按 MicroPatch 处理,不扩大范围。
如果是 DomainConstraint,只沿下面固定链检查一次:
UI → Domain/Application → MES/API → DeviceApply → FocusedTests。

要求:
1. 先找真正拥有该业务规则的 owner;
2. 不要把全仓库所有相同数字机械替换;
3. 明确内部表示和外部契约是否相同;
4. 涉及设备时检查倍率、单位、clamp 和真实能力;
5. 涉及历史配置/数据时才检查兼容;
6. 输出哪些层需要改、哪些层明确不需要改;
7. 完成后只做与这次 constraint 匹配的 focused 验证。

15. 常见反模式

反模式后果
只改 UI 提示后端仍拒绝新值
只改后端,UI 范围没改用户无法输入合法新值
内部单位改了,MES 也机械改破坏外部契约
忽略 DeviceApply 的 clamp设备实际仍收到旧范围
全局替换相同数字修改无关语义
临时测试直接改正式 Domain实验条件污染产品规格
每个数字都全仓库扫描小改成本失控

下一步

一句话原则

先判断一个值是“显示”还是“规则”;是规则就沿固定业务链检查一次,不漏层,也不全仓库漫游。

别来无恙 · C# 上位机 AI 实战站 · 从零到交付 · QQ 群:1016188499