第四章:战略设计之上下文映射

  (banq注:本文类似于微服务和服务集成)

有界的上下文

  这是一种边界(通常是子系统或特定团队的工作)的描述,在边界内定义和使用特定模型。

上游 - 下游
  这表示两个组之间的关系,其中“上游”组的操作影响“下游”组的项目是否能成功,但下游的操作不会显着影响上游项目。(例如,如果两个城市位于同一条河流中,则上游城市的污染主要影响下游城市。)

  上游团队可能独立于下游团队的命运而取得成功。在相互依赖情况下,必须同时交付两个不同环境中的软件开发项目才能被认为是成功的。倡导单向依赖。

自由
  这是一种软件开发环境,其中开发工作的方向,成功或失败在其他环境中对交付几乎没有影响。

上下文映射

  为了策划战略,我们需要一个现实的、大规模的模型开发视图,扩展到我们的项目和我们整合的其他项目。

  在缺乏全局视图的情况下,个别有界的上下文会留下一些问题,其他模型的背景可能仍然模糊不清。

  其他团队中的人员不会非常了解上下文边界,并且会在不知不觉中进行模糊边缘的更改或使互连复杂化。当必须在不同的上下文之间建立联系时,它们往往会相互渗透。

  即使边界清晰,与其他环境的关系也会对模型的性质或可行的变化速度产生约束。这些约束主要通过非技术渠道表现出来,这些渠道有时很难与他们正在影响的设计决策相关联。

因此:

  确定项目中正在使用的每个模型并定义其有界上下文。这包括非面向对象子系统的隐式模型,命名每个有界的上下文,并使名称成为普遍存在的语言的一部分。

  描述模型之间的联系点,概述任何通信的明确翻译,突出显示任何共享,隔离机制和影响级别。

  映射现有形状,稍后进行转换。

  可以作为现实设计战略的基础。

  在以下几页中,关系的表征更具体,在有界上下文之间有一组常见的关系模式。

(banq注:比如订单上下文中需要产品,而产品上下文中产品是一个聚合根,如何在这两者之间进行产品模型映射?关键是通过UML顺序图将模型所在的用例上下文分析出来,顺序图也称为序列图,其实一种时间序列图,也是一种逻辑顺序图,在事件风暴建模中,通过寻找动词事件来发现事件发生的时间先后,找出有界上下文)

#事件风暴

合作关系

  当两个环境中的团队出现成功或失败情况时,经常会因为合作关系。

  在不同的环境中相互依赖的子系统协调不良会导致两个项目的交付失败。一个系统缺少一个关键功能可能会使另一个系统无法传送。与其他子系统的开发人员的期望不匹配的接口可能导致集成失败。一个双方同意的接口可能会变得非常难以使用它会减慢客户端系统的开发速度,或者很难实现它会减慢服务器子系统的开发速度。失败会导致两个项目失败。

因此:

  如果两种情况中的任何一种情况下的开发失败都会导致两者的交付失败,那么在负责这两种情况的团队之间应该建立伙伴关系。建立协调发展规划和一体化联合管理的过程。

  团队必须就其接口的开发进行合作,以满足两个系统的开发需求。应安排相互依赖的功能,以便为同一版本完成它们。

  在大多数情况下,开发人员没有必要详细了解其他子系统的模型,但他们必须协调他们的项目规划。当一个环境中的开发遇到障碍时,需要联合检查该问题,找到一个不会过度妥协任何情境的快速设计解决方案。

  此外,需要一个明确的流程来管理集成。例如,可以定义一个特殊的测试套件,证明接口符合客户端系统的期望,客户端系统可以作为服务器系统上持续集成的一部分运行。

(banq注:微服务之间交互可通过REST API或异步消息,这些都是合作关系的体现)

 

共享内核

  共享模型和相关代码的一部分是一种非常亲密的相互依赖关系,它可以通过设计实现或破坏设计。

  当功能集成受限时,可能会认为一个大的上下文持续集成的开销太高。当团队没有技能或政治组织来维持持续集成,或者当一个团队过于庞大和笨拙时,情况尤其如此。因此可以定义单独的有界上下文并形成多个团队。

  一旦工作在密切相关的应用程序上的独立,不协调的团队可以前进一段时间,但他们生产的产品可能不适合要求,即使是合作伙伴团队也可能最终在翻译层和翻译上花费大量资金,同时重复工作并失去普通无处不在的语言的好处。

因此:

  使用明确的边界指定团队同意共享的域模型的某个子集,保持这个内核很小。

  在此边界内,包括了与模型的此子集一起的代码子集或与模型的该部分相关联的数据库设计。这种明确共享的东西具有特殊的地位,如果不与其他团队协商,不应更改。

  定义一个持续的集成过程,使内核模型保持紧密,并使团队中无处不在的语言保持一致。经常集成功能系统,尽管比团队内持续集成的速度要少一些。

(banq注:微服务不建议共享数据库,可以通过消息系统在应用层共享数据)

客户/供应商开发模式

  当两个团队处于上游 - 下游关系时,上游团队可能独立于下游团队而成功,下游的需求将以各种方式得到解决,并产生广泛的后果。

  下游团队可能无能为力,因为受上游优先事项的影响。同时,上游团队也可能受到抑制,因为担心打破下游系统。比如复杂的审批流程,繁琐的变更请求程序无法改善下游团队的问题。如果下游团队对变更拥有否决权,那么上游团队的随心所欲的开发将会停止。

因此:

  在两个团队之间建立明确的客户/供应商关系,这意味着下游优先考虑上游规划,为下游要求进行谈判和预算任务,以便每个人都了解承诺和时间表。

  敏捷团队可以使下游团队在规划会话中扮演上游团队的客户角色。联合开发的自动验收测试可以验证来自上游的预期接口。将这些测试添加到上游团队的测试套件中,作为其持续集成的一部分,将使上游团队自由进行更改,而不必担心下游的副作用。

 

顺应方式

  当两个开发团队有上游/下游关系时,上游没有动力为下游团队提供需求,下游团队就无能为力了。利他主义可能会激励上游开发商做出承诺,但他们不太可能实现。对这些善意的信任导致下游团队根据永远不可用的功能制定计划。下游项目将被推迟,直到团队最终学会接受它给予的东西。根据下游团队的需求量身定制的接口不在卡片中。

因此:

  通过盲目地遵守上游团队的模型,消除有界上下文之间的翻译的复杂性。虽然这会扼杀下游设计师的风格,但可能无法为应用提供理想的模型,但选择合规性极大地简化了集成。此外,您将与上游团队分享无处不在的语言,上游位于驾驶员座位上,因此最好让他们轻松沟通,利他主义可能足以让他们与你分享信息。
(banq注:建议使用发布/订阅模式)

反腐层

  翻译层可以简单,甚至优雅,当与合作团队建立良好的有界环境时。但是,当控制或通信不足以实现共享内核,合作伙伴或客户/供应商关系时,翻译变得更加复杂,翻译层可采用更加防御的风格。

  具有上游系统的大型接口最终可能完全压倒下游模型的意图,导致其被修改为以临时方式类似于其他系统的模型。遗留系统的模型通常很弱(如果不是大泥球),甚至明显设计的例外可能不符合当前项目的需要,使得符合上游模型是不切实际的。然而,整合可能对下游项目非常有价值甚至是必需的。

因此:

  作为下游客户端,创建隔离层,根据您自己的域模型为您的系统提供上游系统的功能。该层通过其现有接口与另一个系统通信,几乎不需要修改另一个系统。在内部,该层在两个模型之间根据需要在一个或两个方向上平移。

 

开放主机服务

  通常,对于每个有界上下文,您将为每个组件定义一个转换层,您必须在该组件之外集成该转换层。在集成是一次性的情况下,这种为每个外部系统插入转换层的方法可以以最低的成本避免模型的损坏。但是当您发现子系统需求量很大时,您可能需要更灵活的方法。

  当子系统必须与许多其他子系统集成时,为每个子系统定制翻译器会使团队陷入困境。维护越来越多,并且越来越担心何时进行更改。

因此:

  定义一个协议,将您的子系统作为一组服务进行访问,打开协议,以便所有需要与您集成的人都可以使用它。增强和扩展协议以处理新的集成要求,除非单个团队具有特殊需求。然后,使用一次性翻译器来扩充该特殊情况的协议,以便共享协议可以保持简单和连贯。

  这将服务提供者置于上游位置。每个客户都在下游,通常其中一些是顺从的,有些会构建反腐层。具有开放主机服务的上下文可能与其客户端之外的上下文具有任何关系。


发布的语言

  两个有界上下文的模型之间的转换需要一种共同的语言。

  与现有域模型的直接转换可能不是一个好的解决方案。这些模型可能过于复杂或考虑不周。他们可能没有证件。如果将其用作数据交换语言,它基本上会被冻结,无法满足新的开发需求。

因此:

  使用记录良好的共享语言,可以将必要的域信息表达为通用的通信媒介,并根据需要进行语言转换。

许多行业以数据交换标准的形式建立已发布的语言。
项目团队也在自己的组织内开发自己的项目。

发布的语言通常与开放主机服务相结合。

#RESTful
#GraphQL


分开的方式

  在定义需求时,我们必须是无情的。如果两组功能没有显着的关系,它们可以完全相互松散。

整合总是昂贵的,有时效益很小。

因此:

  声明有界上下文与其他上下文无关,允许开发人员在这个小范围内找到简单,专业的解决方案。

 

大泥球*

  当我们调查现有的软件系统时,试图理解在定义的边界内如何应用不同的模型,我们发现系统的一部分,通常是大型系统,其中模型是混合的,边界是不一致的。

很容易陷入困境,试图在没有边界的系统中描述模型的上下文边界。

  明确界定的背景边界只是由于知识选择和社会力量而产生的(即使创建系统的人们当时并不总是有意识地意识到这些原因)。当这些因素缺失或消失时,多个概念系统混合在一起,使定义和规则模糊或矛盾。随着功能的增加,系统可以通过偶然逻辑工作。依赖关系横跨软件。因果关系变得越来越难以追查。最终,该软件凝结成一团泥浆。

对于某些情况来说,泥球实际上非常实用(如Foote和Yoder的原始文章中所述),但它几乎完全阻止了有用模型所需的微妙和精确。

因此:

  在整个混乱中绘制一个边界,并指定它是一个大泥球。不要尝试在此上下文中应用复杂的建模。警惕此类系统蔓延到其他环境的趋势。

#SOA
#单体和微服务:

(banq注:本文部分可以看成微服务的通讯交互以及集成模式,可参考#APISpring Cloud、服务网格等实现)

第五章:战略设计的提炼

领域驱动设计参考

猜你喜欢