敞开生长之旅!这是我参加「日新方案 2 月更文应战」的第 3 天,点击查看活动概况

当说到体系间交互的时候,人们都会想到大名鼎鼎的防腐层,即用一个 Adaptor 进行体系间模型的转化,用来避免其他体系的模型改变对本体系形成影响。可是在实践这个形式的进程中,咱们是否常常遇到如下问题:

  • 事务代码只要三行,模型转化代码却写了几十行
  • 为了给前端增加一个返回值,需求给整条链路上的每个模型增加一个特点,可链路上还会触及好几个体系,一不小心漏加,还会导致 bug
  • 明明是相似的东西,在不同接口中却是不同的类,导致调用方无法一致处理。

这个时候,咱们就应该细心考虑 “防腐层” 是否真的合适了。防腐层一词最早呈现于 Evans 《范畴驱动规划》,英文为 Anticorruption Layer,简称 ACL(下文中都会称之为 ACL),假如去翻阅原著,会发现 “防腐层” 只不过是 Evans 界说的九种体系间联系的一种,在规划体系间联系时,咱们应该依据实际状况灵活挑选,而不是生搬硬套 ACL 来处理一切的体系间联系。

DDD 的战略形式与战术形式:ACL 属于哪种?

毛爷爷曾说“战略上轻视敌人,战术上重视敌人”,战略一般是指比较大的规划,而战术愈加偏向具体执行,在长津湖战役中,彭德怀将美军分割成四个部分别离击破的规划,便是战略,电影中第七交叉连在新兴里击溃美军的进程便是战术。

你真的需要防腐层吗?DDD 系统间的7种关系梳理与实践

具体到软件工程中,战略是指高层视角的体系与人员规划,可能触及多团队的合作联系,往往由架构师决议决议计划;而战术一般是具体的类和方法的规划,开发者一般有更大的话语权。

在《范畴驱动规划》中,前三部分首要讲战术规划,最后一部分专门讲战略规划。

你真的需要防腐层吗?DDD 系统间的7种关系梳理与实践

ACL 这个词呈现在 “战略规划” 部分。这可能违反大多数人的直觉,由于在日常工作中,咱们常常不加考虑地就引进一个 ACL,认为这便是一个简略的规划形式。其实两个体系间是否要引进 ACL,是架构师对于体系间联系深思熟虑的成果,并不是一个简略的代码层面的规划形式。

此处触及到两个相关的 DDD 术语:“限界上下文”(Bounded Context)和“上下文映射图”(Context Map)。一个体系所管理的事务范围称之为“限界上下文”(Bounded Context),而且咱们前文所说的 “体系间联系”,本质上便是两个 “限界上下文” 之间的联系,这种体系间联系在 DDD 中咱们称之为 “上下文映射图”(Context Map),ACL 便是一种 “上下文映射图”。

7 种体系间联系

本节将依据耦合度从高到低逐个讨论这些联系。耦合度高有时并不是坏事,它能够让团队内部的体系愈加内聚,而不是无法整合的碎块。咱们应该依据具体状况进行挑选。

由于体系间联系往往也是安排架构的反映,此处每种联系除了描绘其相关的技能架构,本节也将描绘其适用的安排架构。

同享内核(Shared Kernel)

体系间同享部分模型和相关逻辑,是最亲密的合作联系。

你真的需要防腐层吗?DDD 系统间的7种关系梳理与实践

当负责这两个体系的团队存在十分严密的合作联系(乃至便是同一个团队),而且事务上十分相似时,同享内核便是一个不错的挑选。在责任上,即使同享内核有专门的 owner,其任何修正都需求经过多方讨论,由于其中的任何模型更改都会深刻地影响这几个体系。

最典型的便是事务体系以及事务相关的批量导入导出体系,两者尽管由于技能原因(隔离耗费资源的任务)被区分成了两个体系,可是它们的模型理论上应该彻底同享的(即同享内核),这样才干确保事务上的修正及时反映到导入导出中。试想假如这种场景还用 ACL 做隔离的话,每次事务体系修正,还需求同步在导入导出模型中做修正,这将是十分麻烦的。

客户-供应方(Customer-Supplier)

两个体系尽管相对独立开展,可是底层体系(Supplier, 供应方)愿意为上层体系(Customer, 客户)的需求负责,而且做对应的改变。这也是一种十分亲密的合作联系,只比同享内核弱一点。

由于两个体系在完成时会相互考虑对方,此刻两者交互部分的模型会十分相似,比较同享内核,它只同享一些模型,不同享逻辑代码。

例如,智能办公的表单导出体系依靠表单查找体系查找需求导出的数据,表单查找体系的保护者是同一个团队的开发者,他会接受导出相关的一切查找需求,所以不需求在此基础上再进行任何封装,直接将表单查找体系提供的 QueryCondition 模型作为导出任务模型的一个特点:

public class ExportJob {
    // 来自查找体系的模型
    private QueryCondition queryCondition;
    // 其他特点
    private Long id;
    //...省掉
}

遵奉者(Conformist)

两个体系彻底独立开展,底层体系由于没有人力或许其他原因,不可能由于上层体系的需求做出任何改变。这与下文 ACL 适用的状况相似。

上层体系的模型规划与底层体系的模型严格地保持一致,尽管也是需求一个转化层转化,可是没有做任何“真实”的转化,最多仅仅简略的特点复制,这是和 ACL 的首要差异,ACL 会做愈加杂乱的转化。

例如,钉钉的外部 ISV(三方运用开发商)想要根据钉钉通讯录开发一款运用,由于这个三方运用一切功用都是根据钉钉通讯录,ISV 能够采纳的最合适的方式便是界说一批和钉钉通讯录差不多的模型,这样才干最充分地运用通讯录已有功用,概念上的一致性也有助于和钉钉通讯录开发人员交流。

防腐层(ACL)

ACL 对应的安排架构与遵奉者相似。

ACL 合适两种状况:

  • 相同的概念在两个体系中有不同的意义:“用户”概念在 IM 中便是音讯发起接受方,可是在钉钉通讯录中却是指某个安排内的、含有职位,角色等信息的职员
  • 模型相差巨大:比如 excel 表格和钉钉文档中的表格

ACL 是指杂乱的数据转化层,在转化数据时需求做概念上的翻译。

另谋它路(Separate Way)

“最好”的解耦方法便是彻底另写一套代码。

这有点像 “拆中台” 的思路,研究了半响,发现依靠杂乱的中台,还不如事务团队自己写一套简略的体系完成。

这已经不能算是体系间联系了,是 “彻底没有联系” 的意思。

敞开主机服务(Open Host Service)与发布言语(Published Language)

直接举例,底层服务敞开一个 Http 接口(敞开主机服务),允许以 json 的数据格式(发布言语)进行调用。

它们其实便是敞开 RPC 调用。他们被独自列出来彻底是由于 《范畴驱动规划》这本书成书较早,互联网软件比较少,RPC 也没有特别规范的标准。在微服务年代,限界上下文的调用简直都经过 RPC 进行,而且运用 Hsf, Dubbo 等 RPC 结构将发布言语封装在最底层。这也是我将本条放在最后一个的原因。

总结

软件工程寻求的终极目标便是 “高内聚,低耦合”,翻译成两个正面的指标便是内聚宽和耦:

  • 同享内核:内聚度最高,解耦最差
  • ACL:内聚度最低,解耦最强

你真的需要防腐层吗?DDD 系统间的7种关系梳理与实践

这张图恰恰反映了软件工程没有银弹的道理,经过整理体系间联系,合理的控制体系的内聚与耦合程度,才是项目架构的难点地点。

ACL 被滥用的原因在于开发者将自己写的一小部分代码像“伊甸园”相同保护起来,这是比较简略的处理方式,可是假如要整理怎么和现有事务与体系结合,却要付出很多精力。这就导致体系过度“解耦”,以至于同一个团队保护的事务体系却给调用方一种支离破碎的感觉

因此是否引进 ACL 要依据两个体系当前状况和未来规划决议,假如满足:

  • 同一个团队保护
  • 属于同一个事务
  • 模型差别不大

其中的一到两条。此刻能够考虑暂时不引进 ACL,而是选用同享内核,或许客户-供货商来处理体系间联系。

可是这条规律也只能作为参阅,还是需求结合现实中对于内聚与解耦的需求决议,毕竟软件工程中唯一的规律便是没有规律。

End

作者:元青

微信公众号 「技乐书香」