防腐层在微服务架构中有什么作用?
简化版
防腐层(ACL)位于本领域与外部系统之间,把外部模型、协议和错误语义转换成本领域语言,避免遗留概念渗入核心代码。它隔离变化,却会增加映射、运维和数据一致性成本,不应被做成新的万能共享层。
详细版
落地时由 Adapter 处理 HTTP、RPC 或消息协议,由 Translator 完成字段、枚举、单位与错误码转换,由 Facade 向领域层暴露稳定接口。领域对象不直接依赖外部 DTO;写操作还需定义幂等键、超时后的未知结果和补偿方式。
防腐层适合遗留系统、第三方平台和跨限界上下文集成。若两边模型本来一致且由同一团队同步演进,机械复制一层只会增加维护量;如果映射逻辑包含核心业务决策,则应回到领域服务,而非藏在转换器中。
完整版教学
一、防腐层防的是什么
它防的不是网络攻击,而是模型污染。外部系统把客户叫 party、金额用元字符串、状态用 3 表示已结算,本领域若直接沿用这些定义,外部一次改名就会穿透控制器、服务和数据库。ACL 把这种变化收敛在边界处。
记忆钩子:外部说外部的话,内部说领域的话,翻译只发生在边界。
二、组件如何分工
Domain -> Facade -> Adapter -> External API
^ |
+-- Translator --+
Facade 提供本领域用例,Adapter 处理传输与认证,Translator 做双向模型转换。三者拆开后,协议升级不必改领域接口,字段语义变化也不会塞进网络客户端。
三、转换不只是改字段名
| 外部差异 | ACL 处理 | 不能悄悄处理 |
|---|---|---|
amount="12.34" 元 | 转为 1234 分并校验精度 | 舍入造成金额变化 |
状态码 0/1/3 | 映射领域枚举 | 未知值默认成功 |
| 404 | 转为“资源不存在” | 把超时也当不存在 |
| 批量接口 | 聚合成本领域结果 | 隐藏部分失败 |
例如 12.345 元无法无损转换为分,应明确拒绝或按业务批准的舍入规则处理,不能直接截断。
四、错误与一致性边界
外部超时意味着结果未知,ACL 不能统一包装成“调用失败”后盲目重试。写操作应携带稳定 requestId,保存调用流水,并提供查询或补偿路径;读操作才更适合短重试与缓存降级。
ACL 也不能制造跨系统事务。它可以把 PENDING/CONFIRMED/FAILED 统一成领域状态,但最终一致仍需状态机、Outbox、对账和人工处理闭环。
五、版本演进怎么做
外部 v1 与 v2 可由两个 Adapter 并存,再转换到同一内部模型。灰度期间比较成功率、字段差异和金额校验,确认新链路稳定后下线 v1;内部模型无需同时承受两套 DTO。
若新版本改变业务语义而非格式,例如“取消”从立即生效改为审核中,就应扩展领域状态,不能靠 Translator 把新语义伪装成旧状态。
六、代价与测试
ACL 会增加代码量和一次映射开销,还可能成为吞吐瓶颈。契约测试应覆盖缺字段、未知枚举、精度、时区、部分失败和超时;金丝雀发布时同时观察转换失败率与外部依赖 P99。
测试样例应来自真实契约但脱敏。仅对 Happy Path 做对象映射测试,无法证明线上脏数据和版本共存时仍安全。
运行时还要监控 unknown enum、mapping failure 和外部版本分布;转换失败率突然上升通常意味着对方已变更契约或数据质量。
批量转换要返回逐项结果,不能因一条坏数据把其余成功项误报为全部失败。
七、常见误区与追问
- 误区:防腐层就是 DTO 转换工具。 它还隔离协议、错误语义和调用策略。
- 误区:未知枚举映射成默认值最兼容。 默认值可能把异常状态变成成功。
- 误区:所有外部调用都放入同一个 ACL。 不同上下文会再次耦合成共享单体。
- 误区:ACL 能解决分布式事务。 它只隔离模型,不能提供原子提交。
- 追问:转换失败怎么处理? 拒绝、记录原始版本并告警,不能静默丢字段。
- 追问:何时不需要 ACL? 模型简单稳定、同团队控制且复制成本大于隔离收益时。
- 追问:如何防止 ACL 变胖? 映射留边界,业务规则回领域服务,按上下文拆 owner。
八、加强记忆
按“外部契约—Adapter—Translator—内部模型”记住防腐层:协议、字段和错误在边界转换,业务不变量留在领域内;超时用幂等与补偿收敛,版本用并行 Adapter 迁移。它买到的是独立演进,不是免费的一致性或万能复用。