遗留表中的 ready 字段为 Y,不足以证明设备能接受新的租赁。现有程序可能只检查 ready 与 busy,完全没有读取检验结果和退役标志。把表反向生成类图,会忠实展示这些字段,却不会指出旧判断遗漏了什么。

本篇从一份合成遗留 CSV 和真实执行的旧谓词出发,把设备逐批转换到冻结的27-v1应用。旧字段、未知编码和前后行为差异都保留在实验中。这里没有真实厂商数据,也没有已经获批的生产迁移方案。

从字段追到可观察行为

遗留记录包含 code、ready、busy、inspection、retired 和 note。旧查询的可租条件只有 ready=Y 且 busy=N,设备检验和退役状态不参与判断。四条样本都满足这个布尔条件,但分别表示检验通过、等待检验、已经退役以及未知检验码。

“旧程序这样计算”是一条可以运行验证的事实。“业务就应该这样决定”则是另一项判断。当前系列已经明确待检和退役设备不能新租,因此旧谓词在这两个样本上的放行属于需要改变的行为,不能以保持兼容为由永久保留。

教学输入约定 ready 表示维修侧已经放行,busy 只表示遗留系统记录了某种占用,inspection 是独立检验码,retired 是退役标志。这些含义写入本章合同后才用于翻译。真实迁移必须寻找对应的代码分支、操作记录或业务确认;不能凭英文列名自动得出同样解释。

flowchart LR
  ROW[遗留六字段记录] --> OLD[旧谓词 ready且非busy]
  OLD --> YES[旧结果可租]
  ROW --> ACL[显式翻译规则]
  ACL --> STATE[设备生命周期]
  ACL --> M[维修限制]
  ACL --> Q[未知或信息不足进入隔离]
  STATE --> APP[27-v1预订用例]
  M --> APP
  APP --> NEW[确认或资格拒绝]

字段和行为需要交叉阅读。busy=Y 可能对应一笔租赁,也可能对应维修、调拨或人工锁定;只有一个布尔值时,无法恢复设备、开始时间、结束时间与请求身份组成的承诺。实验遇到这样的行直接隔离,没有补一个虚构的“全天预订”。

候选概念怎样进入翻译边界

翻译器把原 code 映射为带 legacy 前缀的 EquipmentId,以免与新系统已有身份碰撞。检验通过且未退役映射 ACTIVE,等待检验映射 INSPECTION_REQUIRED,退役标志优先映射 RETIRED。维修侧未放行则保持独立限制,不能塞进生命周期枚举后丢掉原因。

EquipmentDescription 使用明确的教学默认描述,不从 note 中猜测设备型号。原始 Row 完整保存在导入记录中,note 可以继续用于追查,但它尚未获得领域字段的地位。把任意备注机械拆成对象属性,会把偶然填写习惯固化成新模型的结构。

这延续 23:翻译边界 的职责:外部模型的功能应通过下游领域的概念表达。Evans 的防腐层讨论也强调隔离与翻译,避免下游结构被上游接口逐步改造成同一个形状。DDD Reference 的 Anticorruption Layer。

本实验的翻译结果包含设备快照、维修限制和原行,不只是一个新的 available 布尔值。生命周期回答设备是否具有出租资格,维修限制回答维修方是否允许释放,真正的租期冲突仍由27调用25的日历判断。多保留这几个区别,才能解释后续拒绝来自哪个条件。

分批导入实际改变什么

第一次迁移只处理 L-A,一条记录进入新快照。第二次处理另外三条:L-B 与 L-C 进入设备集合,L-D 因未知检验码进入隔离集合。最终三个已知设备、一个隔离项;第一批导入的身份仍然存在,初始空快照也没有被就地修改。

导入函数先复制当前映射,再返回新的 State。它同时保留原应用的日历、回执和合同集合,因此迁移设备没有顺手清空租赁历史。这个动作是内存快照的增量迁移,尚未涉及数据库提交、线上双写和断点续传。

flowchart TD
  V0[初始快照 0设备] --> B1[第一批 L-A]
  B1 --> V1[快照1 1设备]
  V1 --> B2[第二批 L-B L-C L-D]
  B2 --> V2[快照2 3设备]
  B2 --> Q[隔离 L-D及原因]
  V2 --> REPLAY[原行再次导入]
  REPLAY --> SAME[身份与状态不重复]
  V2 --> CHANGE[相同code但原行变化]
  CHANGE --> REVIEW[要求对账 不覆盖]

同一批出现重复 code 时,两条都不导入,避免由输入顺序决定哪一条覆盖另一条。目标侧已经存在同一个映射身份,但没有相应导入记录,也拒绝插入。原行重放则跳过,不重复增加设备;原行发生变化时,要求另行对账,不拿新字段直接覆盖已经进入新系统的状态。

这几个条件把“再次运行安全”分解成可检查的行为。重复执行相同文件和追踪不断变化的遗留库是两项不同任务。后者还需要来源版本、更新顺序和冲突政策,本实验没有用一次批处理通过来声称已经解决持续同步。

隔离项应进入明确的处理队列,而不能只计入一条失败总数。未知检验码需要补充枚举含义,占用记录需要寻找租期与请求来源,重复身份则需要确定两行是否指向同一台设备。这三类问题需要不同材料,直接重试同一翻译函数不会增加任何信息。

用领域场景检查翻译是否有用

三个已知设备导入后,实验构造真实的 RentalApplication,将翻译出的维修限制传给 MaintenancePort,再分别提交相同时间窗口的租赁。L-A 返回 CONFIRMED,L-B 与 L-C 返回 INELIGIBLE;旧谓词对三者均返回 true,差异在 SHADOW 输出中逐条记录。

这里的“影子比较”只是两套本地计算结果的对照,没有复制线上流量或自动切换用户请求。它证明所选样本发生了预期语义变化;是否允许以这些变化替换既有业务,仍需要真实项目的验收依据。

未知检验码没有被映射成待检后偷偷继续。那样虽然通常比直接放行保守,却仍然替未知值指定了一种业务含义。隔离结果保留原身份和原因,让后续补证有明确对象。直接调用 translate 处理这个未知值会抛出异常,独立负例命令实际以非零码结束。

翻译成功也不是永久资格认证。迁移后设备可能退役,维修限制可能过期,租期可能被其他请求占用。新系统必须继续执行自己的用例规则,不能把“导入成功”存成绕过资格检查的通行标记。这也是 28:可用性模型 中快照查询与预订决定的区别。

增量替换需要保留哪些退路

Fowler 对 Strangler Fig 的说明强调逐步迁移行为,以及新旧系统共存期间的过渡结构。这个方向能帮助划分替换范围,但不会自动决定每一行数据的语义或双写策略。Strangler Fig。

本例先迁移来源可解释、没有遗留占用的设备,把信息不足的记录留在旧侧。已迁移设备需要指定单一写入归属;否则旧系统再次改 busy,新系统同时接受预订,两边都可能依据过期事实行动。当前实验没有并行写入者,因此这项归属只作为后续切换条件,没有被标为已经实现。

切换单位也应对应一项可独立核对的能力。本例选择设备资格导入,未迁移费用结算和维修工单。即使某个旧界面同时展示这三类数据,也不必一次把它们全部替换;但新请求路由到哪一侧、哪一侧允许写入,需要能按设备身份明确回答。

已有合同金额不应随着设备资料导入重新计算。迁移代码直接保留旧应用的合同集合,避免当前目录价格进入历史草案。若实际项目要求同步迁移金额或请求身份,就需要补充独立的金额一致性和重放场景,不能把设备条数相等当作整个业务数据完整。

退路也不能只写“回滚”。尚未在新系统发生业务变化时,可以丢弃候选快照重新导入;已经发生预订之后,恢复旧空快照会丢失承诺和回执。此时需要对账、转换或补偿,而不是恢复一个旧文件名。实验保留原行和导入身份,提供追查起点,但没有实现跨系统补偿。

34:受限往返 已展示从代码结构反读模型会丢失额外行为。遗留表也一样:字段图可以作为调查索引,不能取代“哪个调用者在什么条件下写这个值”的证据。没有解释的字段应保留未知状态,避免将推测混入正式合同。

复跑与交付范围

代码位于 examples/software-modeling/labs/37/,输入与字段约定位于 models/37/。将 JAVA_HOME 指向 JDK 21 后运行:

1
2
bash examples/software-modeling/labs/37/run.sh
bash examples/software-modeling/labs/37/run.sh unknown

正向入口完成十七项检查,覆盖分批导入、原值重放、三台设备真实预订、未知码隔离、来源变化、重复身份和目标冲突。第二条在完成同样检查后直接翻译未知行,预期失败。原始输出和退出码位于 evidence/37/。

下载遗留样本、翻译器及迁移证据。CSV 读取器只服务六列可信教学样本,没有实现通用转义格式;State 也不是外部数据导入协议。27-v1源码保持不变,其基线在临时复制目录中另行复跑,原章节证据未被覆盖。