第 15 章 —— 通过领域适配器隔离 DSM 细节
本章目标: 本章完成后,开发者能够用领域端口封装 DSM 三类 typed handles,把 locator、生命周期和失败翻译集中在适配器边界内。
学习目标
- 识别 DSM API 泄漏进业务层的维护代价。
- 设计只使用业务语言的
FulfillmentCoordination端口。 - 把 Register、Lease、CRDT handle 与 locator 集中到 composition root。
- 分开验证领域决策和真实 Runtime 行为。
前置条件
- 已完成第 9 章的集合选择。
- 已理解第 14 章的失败与业务承诺边界。
- 能运行
examples/order-fulfillment-control-planeMaven 示例。
案例进度
前三篇直接操作集合是为了学习语义。现在履约服务要进入真实工程:业务用例只说「发布路由」「领取分片」「记录请求」,不再知道
shared/gateway/route-hints 或
LeaseAcquireOptions。
业务端口拥有语言,适配器拥有技术细节
书籍示例定义的端口很小:
public interface FulfillmentCoordination {
void publishRoute(String serviceKey, String address);
Optional<String> routeFor(String serviceKey);
ShardClaim claimShard(String shardId);
long incrementRequestCount();
}端口不返回
DsmRegister、LeaseSnapshot、PnCounterState
或 CollectionLocator。业务层因此能用 fake 直接测试;DSM
适配器负责把结果翻译成
ShardClaim(granted, uncertain, fencingToken, reason)。
Composition root 是唯一装配点
DsmFulfillmentModule 集中拥有:
DsmRuntime的创建、启动和关闭;clusterId与serviceId;- 三个稳定 locator 与 schema id;
- Register、Lease、CRDT 的 entity/codec/spec;
- 三个 typed handles 到领域端口的映射。
这不是为了隐藏所有 DSM 概念。locator 与 schema 仍是重要合同,只是由适配器拥有,而不是散落到 controller、service 和 job 中。
失败翻译不能抹掉不确定性
Lease acquire 有
granted、uncertain、snapshot 和
reason。适配器若只返回
boolean,调用方无法区分明确拒绝与结果不确定。领域结果应保留业务决策实际需要的信息,同时阻止调用方任意操作底层
handle。
同样,fencing token 需要穿过端口进入受保护的下游写入;把 token 藏掉会让第 7 章的安全边界失效。示例当前返回 token,但没有实现真实下游 guard,因此验收卡明确不声称已完成外部 fencing。
两类测试,各自只证明一层
示例有两个测试:
- 用
RecordingCoordination验证FulfillmentService只依赖领域端口。这是 E1。 - 用真实
DsmRuntime和三类集合验证适配器。这是单进程 E2。
如果只写第二类测试,业务决策和 DSM 装配会粘在一起;如果只写第一类,fake 全绿也不能证明真实 spec、codec 或 Lease 能运行。
反例与故障注入
- 把 locator 字符串复制到
FulfillmentService,然后改变 collectionId,观察需要修改多少业务文件。 - 把
LeaseAcquireResult压成 boolean,尝试表达uncertain=true。 - 忘记
runtime.start(),检查生命周期错误落在哪一层。 - 让业务层直接调用
requestCounter.state(),观察领域测试如何被 CRDT 类型污染。
实验
cd examples/order-fulfillment-control-plane
mvn clean test阅读
port-contract.json,确认每个业务动作都有领域输入、领域输出、DSM
handle 和未被适配器承担的外部责任。
实验验收卡
| 字段 | 内容 |
|---|---|
| 运行命令 | cd examples/order-fulfillment-control-plane && mvn clean test |
| 输入或故障 | fake 端口;真实单节点 Runtime;三类 typed handles |
| 可观察结果 | 领域服务不导入 DSM 类型;真实适配器完成路由、租约与计数操作 |
| 证据等级 | E1 领域单元测试 + E2 单进程 Runtime 行为 |
| 本实验未证明 | 多节点传播、repair、真实网络、下游 fencing 或生产装配 |
回顾
- 领域端口拥有业务语言,适配器拥有 DSM 细节。
- locator、schema、codec 和生命周期应集中在 composition root。
- 失败翻译要保留不确定性和 fencing token。
- fake 与真实 Runtime 测试分别回答不同问题。
下一步
第 16 章把手工 composition root 切换为 Spring Boot 属性和条件 bean,并验证实际上下文能启动。