[译文]Domain Driven Design Reference(五)—— 为战略设计的上下文映射

本书是Eric Evans对他自己写的《领域驱动设计-软件核心复杂性应对之道》的一本字典式的参考书,可用于快速查找《领域驱动设计》中的诸多概念及其简明解释。

 

 

其它本系列其它文章地址:

[译文]Domain Driven Design Reference(一)—— 前言

[译文]Domain Driven Design Reference(二)—— 让模型起作用

[译文]Domain Driven Design Reference(三)—— 模型驱动设计的构建模块

[译文]Domain Driven Design Reference(四)—— 柔性设计

[译文]Domain Driven Design Reference(五)—— 为战略设计的上下文映射 

 

绑定上下文

  对一个特定模型的定义和适用范围(通常是一个子系统,或特定团队的工作)的描述。

 

上游-下游

  两个组之间的关系是“上游”小组的行为影响“下游”小组的项目成功。但下游的行为并不会显著影响上游项目。(例如,如果两个城市沿着同一条河流,上游城市的污染主要影响下游城市。)

  上游团队可以独立于下游团队的命运而取得成功。

 

相互依赖

  必须在不同的上下文中交付两个软件开发项目以使其中任何一个被认为是成功的情况。(当两个系统各自依赖另一个系统的信息或功能时,我们通常会尽量避免将看到的项目构建成相互依赖的。 然而,也有一些相互依赖的项目,系统依赖性只向一个方向发展。当依赖系统没有其它的系统与该系统的集成时,它几乎没有任何价值,或许因为这是唯一一个使用它的地方,那么未能提供依赖系统就会导致两个项目都失败。)

 

自由

  一个理想中的软件开发上下文,在其它上下文中的开发工作是成功或失败对其自己的交付没有什么影响。

 

上下文映射

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

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

  其他团队的人不会意识到上下文的界限,并且会在不知不觉中做出一些模糊边缘或使内部连接复杂化的改变。当连接必须在不同的上下文中进行时,它们往往会相互渗透。

  即使边界清晰,与其他上下文的关系也会限制模型的性质或可行的变化速度。这些制约因素需要通过非技术渠道表现出来,有时很难与他们正在影响的设计决策联系起来。

  因此:

  识别项目中正在使用的每个模型并定义它的限界上下文。这包括非面向对象子系统的隐式模型。给每个限界上下文命名,并且使其名称成为通用语言的一部分。

  描述模型之间的联系点,列出对任何交互的明确翻译,突出任何共享、隔离机制和影响程度。

  映射现有的领域范围。稍后再进行转换。

    这张映射图可以成为实际设计策略的基础。

  在接下来的几页中,关系的描述会变得更加具体,在限界上下文之间有一组通用的关系模式。

 

合作关系*

  当两个上下文中的团队共同成功或失败时,通常会出现合作关系。

  在相互独立的上下文中,相互依赖的子系统缺少协作会导致两个项目的交付失败。一个系统缺失的一个关键特性可能会使另一个系统无法交付。不符合其他子系统开发人员期望的接口可能导致集成失败。一个相互约定的接口可能会变得过于别扭,以致于减慢了客户端系统的开发速度,或者很难实现,从而减慢了服务端子系统的开发速度。失败带来了两个项目的失利。

  因此:

  如果两个上下文中的任何一个开发失败都将导致两个上下文的交付一起失败,则在负责这两个上下文的小组之间建立合作关系。制定协调发展和联合管理一体化的过程。

  团队必须在其接口的演进上进行协作,以适应这两个系统的开发需求。应该安排相互依赖的feature,以便它们在同一版本中完成。

  大多数情况下,开发人员不需要详细了解其他子系统的模型,但他们必须协调他们的项目计划。当一个上下文中的开发遇到障碍时,则需要联合研究这个问题,以找到一种紧急的设计解决方案,而不会过分地损害任何一方。

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

 

共享内核

  共享模型和相关代码的一部分是非常密切的相互依赖关系,它能够加快设计工作或者破坏这些共享的东西。

  当功能集成受到限制时,大型上下文的持续集成的开销可能会被认为太高。当团队没有足够的技能或组织架构来维持持续集成,或者单个团队的规模太大而笨拙时,这种情况可能尤为明显。因此,可以定义独立的限界上下文,并形成多个团队。

  一旦独立的、不协调的团队在密切相关的应用程序上工作,可能会向前推进一段时间,但是他们生产的产品可能不适合在一起。即使是合作伙伴团队最终也会花费大量精力在翻译层和改造上,同时重复这些工作并失去通用语言的好处。

  因此:

  用明确的边界指定团队同意分享的领域模型的一部分子集。保持这个内核尽可能的小。

  在这个边界内,包括模型的子集,代码的子集,或者与该模型的部分相关联的数据库设计。这种显式共享的内容具有特殊的地位,在未与其他团队协商的情况下不应改变。

  定义一个持续集成过程,以保持内核模型的紧凑性,并与团队的通用语言保持一致。经常其整合功能系统,虽然比团队中持续集成的次数要少一些。

 

客户/供应商开发

  当两个团队处于上下游关系时,上游团队可能独立于下游团队的命运而取得成功,下游的需求将以各种各样的方式得到解决,并带来广泛的负面后果。

  下游的团队可能是无助的,受上游优先级的摆布。与此同时,上游团队可能会收到抑制,担心会破坏下游系统。拥有复杂审批流程的繁琐的变更请求过程并没有改善下游团队的问题。如果下游团队对变更拥有否决权,上游团队的自由发展就会停止。

  因此:

  在两个团队之间建立清晰的客户/供应商关系,意味着将下游优先因素放到上游的规划中。为下游需求进行谈判和预算任务,以便每个人都了解承诺和时间表。

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

 

顺从者

  当两个开发团队有一个上下游关系时,上游没有动力为下游团队的需求提供帮助,下游团队就无能为力了。利他主义可能会促使上游开发者做出承诺,但它们不太可能实现。相信这些好意会导致下游团队基于无法获得的特性来制定计划。下游项目将被推迟,直到团队最终学会接受上游所提供的东西。针对下游团队的需求量身定制的接口是不太不可能的。

  因此:

  通过对上游团队的模型进行严格的遵守,消除了限界上下文之间的转换的复杂性。尽管这限制了下游设计人员的风格,并且可能不会产生应用程序的理想模型,但是选择一致性极大地简化了集成。此外,你将与上游团队共享一种通用语言。上游在驾驶员的位置上,所以让他们的交流变得容易是件好事。利他主义可能足以让他们与你分享信息。

 

反腐层

  当与合作团队衔接良好设计的限界上下文时,翻译层可以是简单的,甚至是优雅的。但是,当控制或通信不足以实现共享内核、合作伙伴或客户供应商关系时,转换就变得更加复杂。翻译层采用了一种更具防御性的语气。

  一个提供给上游系统的大型接口最终可能完全颠覆下游模型的意图,从而使其被修改成以一种特别的方式来模仿其他系统的模型。遗留系统的模型通常是很薄弱的(如果不是大泥球的话),即使是明确设计的例外也可能不符合当前项目的需求,这使得遵循上游模型变得不切实际。然而,这种集成对于下游项目可能非常有价值甚至是必需的。

  因此:

  作为下游客户端,创建一个隔离层,根据您自己的领域模型,为系统提供上游系统的功能。该层通过其现有的接口与另一个系统进行通信,只需要很少或不需要对其他系统进行修改。在内部,这一层在两个模型之间需要单向或双向转换。

 

开放主机服务

  通常对于每个限界上下文,您将为每个部件定义一个翻译层,您必须将其与上下文之外的组件集成在一起。在集成是一次性的情况下,为每个外部系统插入翻译层的这种方法以最小的成本避免了模型的损坏。但是当你发现你的子系统有更高的要求时,你可能需要更灵活的方法。

  当一个子系统必须与许多其他的子系统集成时,为每一个子系统定制一个翻译对象可能会使团队陷入困境。有越来越多的维护,越来越多的担心什么时候会发生变化。

  因此:

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

  这将使服务提供者处于上游位置。每个客户端都在下游,并且通常其中一些客户端会遵守规定,有些客户端会建立反腐层。具有开放主机服务的上下文可能与它的客户端以外的上下文有任何关系。

 

公共语言

  两个限界上下文模型之间的转换需要一种通用语言。

  直接转换到现有的领域模型可能不是一个好的解决方案。这些模型可能过于复杂或被分解得很糟糕。也许他们说的是非法的。如果将其中一种用作数据交换语言,它实际上就会被冻结,不能响应新的开发需求。

  因此:

  使用一种文档完整的公共语言,可以将必要的领域信息作为一种通用的通信媒介来表达,并根据需要翻译为该语言。

  许多行业以数据交换标准的形式建立了公共语言。项目团队也开发自己的,在他们的组织内使用。

  公共语言通常与开放主机服务相结合。

 

分而治之

  在定义需求方面,我们必须冷酷无情。如果两组功能之间没有显著的关系,它们可以完全相互分离。

  整合总是代价很大的,有时候好处很小。

  因此:

  声明一个限界上下文,使其与其他上下文完全没有关联,允许开发人员在这个小范围内找到简单的、专门的解决方案。

 

大泥球

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

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

  定义良好的上下文边界作为知识选择和社会力量的结果出现(尽管创建系统的人在当时可能并不总是有意识地意识到这些原因)。当这些因素缺失或消失时,将多个概念系统混合在一起,使得定义和规则变得模棱两可或相互矛盾。随着特性的添加,系统是根据附加的逻辑来工作的。依赖关系纵横交错。因果关系变得越来越难以追踪。最终,软件会凝结成一个大的泥球。

  在某些情况下,大球泥实际上是非常实用的(正如Foote和Yoder的原文所描述的那样),但它几乎完全阻止了有用模型所需要的敏锐和精确性。

  因此:

  在整个混乱的周围画一个边界,把它指定为一个大泥球。不要尝试在此上下文中应用复杂的建模。要警惕这种系统向其他上下文蔓延的趋势。

  (见http://www.laputan.org/mud/mud.html。Brian Foote和Joseph Yoder)

 

 

 

作者:Zachary
出处:https://zacharyfan.com/archives/305.html

 

 

▶关于作者:张帆(Zachary,个人微信号:Zachary-ZF)。坚持用心打磨每一篇高质量原创。欢迎扫描右侧的二维码~。

定期发表原创内容:架构设计丨分布式系统丨产品丨运营丨一些思考。

 

如果你是初级程序员,想提升但不知道如何下手。又或者做程序员多年,陷入了一些瓶颈想拓宽一下视野。欢迎关注我的公众号「跨界架构师」,回复「技术」,送你一份我长期收集和整理的思维导图。

如果你是运营,面对不断变化的市场束手无策。又或者想了解主流的运营策略,以丰富自己的“仓库”。欢迎关注我的公众号「跨界架构师」,回复「运营」,送你一份我长期收集和整理的思维导图。

posted @ 2018-06-01 06:48  Zachary_Fan  阅读(805)  评论(0编辑  收藏