第15章 精炼

     上章我们讲到领域层被分割成一个个BOUNDED CONTEXT,构建出了CONTEXT MAP,并介绍了各个CONTEXT的关系。做任何事情,都要抓住主要矛盾,进行领域驱动设计也是。我们需要通过精炼,得到CORE DOMAIN(核心领域)。围绕核心领域展开分析。核心领域可能包含几个CONTEXT,也肯能只包含一个CONTEXT,甚至可能只是CONTEXT的一部分(如上章讲的SHARED KERNEL)。

       精炼是把一堆杂乱在一起的组件分开的过程,以便通过某种形式从中提取出最重要的内容。就像蒸馏过程一样。精炼把最有价值的那部分提出取出来。这个部分使我们的软件区别于其他软件,并使整个软件产生价值。这个部分就是CORE DOMAIN。

       精炼过程所分离出来的副产品也有一定的价值,比如本章将介绍的GENERIC SUBDOMAIN和COHERENT MECHANISM。

      本章为了更好的说清楚各部分的关系,我将相关概念的顺序调整一下,不完全按照书中的顺序进行介绍。

15.1 如何提炼CORE DOMAIN

15.1.1找准方向

通过DOMAIN VISION STATEMENT(领域愿景说明) HIGHLIGHTED CORE(突出核心),找准核心领域的提炼方向。

DOMAIN VISION STATEMENT(领域愿景说明)

作者在书中提到“很多团队都会编写‘愿景说明’,愿景说明展示了应用程序为组织带来的具体价值”。他这里提到的“愿景说明”,大家可以理解为项目的目标,是一个非常宏观的概念。比如,“便于中小企业融资”这个目标。一些愿景说明文档在项目启动以后就弃之不用了,而在实际开发中也不会使用他,甚至根本不会有技术人员去阅读它。DOMAIN VISION STATEMENT(领域愿景说明)就是模仿这类文档创建的,但他关注的重点是领域模型的本质。在项目开发的所有阶段,管理层和技术人员都可以直接用领域远景说明来指导资源分配、建模选择和团队成员的培训。如果领域模型为多个群里提供服务,那么此文档还能够显示出各个群里的利益使如何分配的。这份文档要尽量精简,并且能将你的领域模型与其他领域模型区分开。它可以作为一个指南,帮助开发团队在精炼模型和代码的过程中保持统一的方向。大家可以随着新的理解随时修改它。

下面展示了两个DOMAIN VISION STATEMENT的示例:

HIGHLIGHTED CORE(突出核心)

 DOMAIN VISION STATEMENT从宽泛的角度对CORE DOMAIN进行了说明,但CORE DOMAIN到底包含哪些元素,不同的人会有不同的理解,甚至同一个人在不同的时间也会有不同的理解。所以我们需要创建一个单独的文档(精炼文档)来描述和解释CORE DOMAIN。精炼文档并不是完备的设计文档,简短一些(3-7页,每页内容不必太多),主要是要描述好CORE DOMAIN内各元素之间的关系。精炼文档是HIGHLIGHTED CORE(突出核心)的一种形式。

    精炼文档应该能够被团队中的非技术人员理解。把它当作一个共享的视图,描述每个人都应该知道的东西,而且可以把它作为团队所有成员研究模型和代码的一个起点。开发人员通过精炼文档应该很容易分别出什么在核心领域内,什么在核心领域外。精炼文档要保证和CORE DOMAIN的一致。CORE DOMAIN发生变更后,要及时更新精炼文档。

15.1.2去粗取精

有时候,我们很难说明白哪部分属于核心领域,但是反过来,我们很容易说清楚,什么不属于核心领域。

15.1.2.1 COHESIVE MECHANISM(内聚机制 )

用COHESIVE MECHANISM(内聚机制 )把“做什么”和“如何做”分开。但“如何做”还是在CORE DOMAIN之内的。所以COHESIVE MECHANISM划分出的部分,还是CORE DOMAIN之内。将如何做内聚到更小的模块的机制,就叫“内聚机制”。比如排序算法,或者(作者举的对图的运算的相关算法。

15.1.2.2 GENERIC SUBDOMAIN(通用子领域)

把模型中起支撑作用的部分,提取出来,放在单独的MODULE中,这些部分就是GENERIC SUBDOMAIN(通用子领域)。GENERIC SUBDOMAIN的优先级低于CORE DOMAIN。通用不等同于可重用。我们应该将更多的精力放在CORE DOMAIN。GENERIC SUBDOMAIN只需保证服务好对应的CORE DOMAIN,不必服务于其他领域。GENERIC SUBDOMAIN与COHESIVE MECHANISM的动机是相同的——都是为了CORE DOMAIN减负。区别在于二者所承担的职责的性质不同。GENERIC SUBDOMAIN是以CORE DOMAIN为基础的,表示如何看待领域的某个方面。在这一点上,他和CORE DOMAIN没什么区别,只是重要性和专门程度较低而已。COHESIVE MECHANISM并不表示领域,他的目的是解决CORE DOMAIN所提出来的一些复杂的计算问题。

具体的落地方面GENERIC SUBDOMAIN有4种实现思路。

思路1:现成的解决方案

有时可以购买一个已实现好的解决方案,或使用开源代码。

优点

  • 可以减少代码的开发;
  • 维护负担转移到了外部;
  • 代码已经在很多地方使用过,可能较为成熟,因此比自己开发的代码更可靠和完备。

缺点

  • 在使用之前,仍需要花时间来评估和理解它;
  • 就业内目前的质量控制水平而言,无法保证它的正确性和稳定性;
  • 它可能过于细致了(远远超出了你的目的),集成的工作可能比开发一个最小化的内部实现更大;
  • 外部元素的集成尝尝不顺利。他可能有一个与你的项目完全不同的BOUNDED CONTEXT。即使不是这样,他也很难顺利地引用你的其他软件包中的ENTITY。
  • 他可能会引入对平台、编译器版本的依赖。

思路2:公开发布的设计或模型

优点

  • 比自己开发的模型更为成熟,并且反映了很多人的深层知识;
  • 提供了随时可用的高质量文档。

缺点

  • 可能不是很符合你的需要,或者设计得过于细致了(远远超出了你的需要)。

    作者引用了一句哲言:“抄袭!抄袭!不要让任何人的工作逃过你的眼睛……但一定要把这叫做研究”。在领域建模中,特别是在攻克GENERIC SUBDOMAIN时,这是金玉良言。作者在书中提到“可以重视并利用《分析模式》一书中的模型(再次证明《分析模式》这本书的价值还是很大的)”。

思路3:把具体的实现外包出去

优点

  • 使核心团队可以脱身去处理CORE DOMAIN,那才是最需要知识和经验积累的部分;
  • 开发工作的增加不会使用团队规模无限扩大下去,同时又不会导致CORE DOMAIN知识的分散;
  • 强制团队采用面向接口的设计,并且有助于保持子领域的通用性,因为规格已经被传递到外部。

缺点

  • 仍需要核心团队花费一些时间,因为他们需要与外包人员商量接口、编码标准等内容;
  • 当把代码移交回团队时,团队需要耗大量的精力来理解这些代码。
  • 无法保证代码质量。

比如在之前的项目中,我们把APP的开发全都外包了出去。因为APP只涉及到了数据的展示和收发,不涉及CORE DOMAIN的内容。

思路4:内部实现

优点

  • 易于集成
  • (相比使用成熟框架来说)只开发自己需要的,不做多余的工作;

缺点

  • 需要承受后续的维护和培训负担;
  • 很容易低估开发这些软件包所需的成本和时间。

当然,“内部实现”也可以与“公开发布的设计或模型”结合起来使用。

GENERIC SUBDOMAIN是你充分利用外部设计专家的地方,这些专家不需要深入理解你特有的CORE DOMAIN,也不涉及CORE中的具体机密。随着时间的推移,CORE DOMAIN会不断变窄,越来越多的通用化的模型将作为框架被实现出来,或者至少被实现为公开发布的模型或分析模式。

15.1.2.3 SEGREGATED CORE(分离核心)

SEGREGATED CORE是对CORE DOMIAN 的进一步剥离。SEGREGATED CORE也起到了对CORE DOMIAN的支撑作用,但不是不像GENERIC SUBDOMAIN那样很容易识别出来,甚至代码包都是和CORE DOMAIN藕合在一起的。

具体分离方法上,基本上采用了和GENERIC SUBDOMAIN一样的原则,只是从另一个方向来考虑而已。那些在应用程序中非常关键的内聚子领域可以被识别出来,并分离到它们自己的内聚包中,如何处理剩下的那些难以区分的元素虽然也很重要,但其重要性略低。这些元素或多或少地可以保留在原先的位置,也可以放在包含了重要类的包中。最后,越来越多的剩余元素可以被提取到GENERIC SUBDOMAIN中。SEGREGATED CORE可以理解成从CORE DOMIAN分离出来的,向GENERIC SUBDOMAIN转化前的中间状态。此状态的有一部分可以转为GENERIC SUBDOMAIN,剩下的部分还处于SEGREGATED CORE状态。

15.2 对CORE DOMAIN进一步抽象

即使是CORE DOMAIN模型,其内部也可能会包含太多的细节,以至于它很难表达出整体的视图。我们需要采用ABSTRACT CORE 模型对其进行进一步的抽象,将模型中最基本的概念识别出来,并分离到不同的类、抽象类或接口中。设计这个抽象模型,能够表达出重要组件之间的大部分交互。把这个完整的抽象模型放到ABSTRACT CORE,而专用的、详细的实现类则留在子领域的CORE中。ABSTRACT CORE和子领域的CORE共同构成CORE DOMAIN。

  提取ABSTRACT CORE并不是一个机械的过程,需要通过重构得到跟深层的理解,而且往往需要大量的重新设计。如果项目中同时使用了ABSTRACT CORE和精炼文档,那么ABSTRACT CORE的最后结果看起来应该和精炼文档非常类似。当然ABSTRACT CORE使用代码编写的,因此更为完整。

15.3 如何管理CORE DOMAIN

我们一定要将CORE DOMAIN交给技术能力最强、最稳定的的团队或人员。他们需要不断积累和消化专业知识,并将这些知识转化为一个丰富的模型。很多团队喜欢把一些通用话的模块(而不是CORE DOMAIN)交给技术大牛,而往往技术大牛也乐于做这些工作。但是在领域驱动模式的开发方式下,这种工作的分配是不可取的。

更多推荐

解读《领域驱动设计 软件核心复杂性应对之道》(七)