软件的核心是其为用户解决领域相关的问题的能力。所有其他特性,不管有多么重要,都要服务于这个基本目的。——EricEvans,《领域驱动设计》
应对软件复杂度,许多顶尖的软件设计人员与开发人员纷纷结合实践提出自己的真知灼见,既包括编程思想、设计原则、模式语言、过程方法和管理理论,又包括对编程利器自身的打磨。毫无疑问,通过这些真知灼见,软件领域的先行者已经改变或正在改变我们构建软件的方法、过程和目标,我们欣喜地看到了软件的构建正在向着好的方向改变。然而,整个客观世界的所有现象都存在诸如黑与白、阴与阳、亮与暗的相对性,任何技术的发展都不是单向的。随着技术日新月异向前发展,软件系统的复杂度也日益增长。中国有一句古谚:“道高一尺,魔高一丈。”又有谚语:“魔高一尺,道高一丈。”究竟是道高还是魔高,就看你是站在“道”的一方,还是“魔”的一方。
在构建软件的场景中,软件复杂度显然就是“魔”,控制软件复杂度的方法则是“道”。在软件构建领域,“道”虽非虚无缥缈的玄幻叙述,却也不是绑定在具象之上的具体手段。软件复杂度的应对之道提供了一些基本法则,这些基本法则可以说放之四海而皆准,其中一条基本法则就是:能够控制软件复杂度的,只能是设计(指广泛意义上的设计)方法。因为我们无法改变客观存在的问题空间(参见2.1.2节对问题空间和解空间的阐释),却可以改变设计的质量,让好的设计为控制复杂度创造更多的机会。如果我们将软件系统限制在业务软件系统之上,又可得到另外一条基本法则:“要想克服”(业务系统的)复杂度,就需要非常严格地使用领域逻辑设计方法。[8]1在近20年的时间内,一种有效的领域逻辑设计方法就是EricEvans提出的领域驱动设计(domain-drivendesign)。
EricEvans通过他在2003年出版的经典著作《领域驱动设计》(Domain-DrivenDesign:Tackling Complexity in the Heart of Software)全方位地介绍了这一设计方法,该书的副标题旗帜鲜明地指出该方法为“软件核心复杂性应对之道”。
领域驱动设计究竟是怎样应对软件复杂度的?作为一种将“领域”放在核心地位的设计方法,其名称足以说明它应对复杂度的态度。用EricEvans自己的话来说:“领域驱动设计是一种思维方式,也是一组优先任务,它旨在加速那些必须处理复杂领域的软件项目的开发。为了实现这个目标,本书给出了一套完整的设计实践、技术和原则。”
领域驱动设计概览
结合我们通过理解能力和预测能力两个维度对软件系统复杂度成因的剖析,确定了影响复杂度的3个要素:规模、结构与变化。控制复杂度的着力点就在这3个要素之上!领域驱动设计对软件复杂度的应对,是引入了一套提炼为模式的设计元模型,对业务软件系统做到了对规模的控制、结构的清晰化以及对变化的响应。
要深刻体会领域驱动设计是如何控制软件复杂度的,还需要整体了解EricEvans建立的这一套完整的软件设计方法体系,包括该方法体系提出的设计概念与设计过程。
领域驱动设计的基本概念
领域驱动设计作为一个针对大型复杂业务系统的领域建模方法体系(不仅限于面向对象的领域建模),它改变了传统软件开发工程师针对数据库建模的方式,通过面向领域的思维方式,将要解决的业务概念和业务规则等内容提炼为领域知识,然后借由不同的建模范式将这些领域知识抽象为能够反映真实世界的领域模型。
EricEvans之所以提出这套方法体系,并非刻意地另辟蹊径,创造出与众不同的设计方法与模式,而是希望恢复业务系统设计核心关注点的本来面貌,也就是认识到领域建模和设计的重要性,然而在当时看来,这却是全新的知识提炼。正如他自己所云:“至少20年前,一些顶尖的软件设计人员就已经认识到领域建模和设计的重要性,但令人惊讶的是,这么长时间以来几乎没有人写出点儿什么,告诉大家应该做哪些工作或如何去做……本书为做出设计决策提供了一个框架,并且为讨论领域设计提供了一个技术词汇库。”这里提到的“技术词汇库”就是我提到的设计元模型。
领域驱动设计元模型
领域驱动设计元模型是以模式的形式呈现在大家眼前的,由诸多松散的模式构成,这些模式在领域驱动设计中的关系如图所示。
领域驱动设计的核心是模型驱动设计,而模型驱动设计的核心又是领域模型,领域模型必须在统一语言的指导下获得。为整个业务系统建立的领域模型要么属于核心子领域,要么属于通用子领域。之所以区分子领域,一方面是为了将一个不易解决的庞大问题切割为团队可以掌控的若干小问题,达到各个击破的目的,另一方面也是为了更好地实现资产(人力资产与财力资产)的合理分配。
为了保证定义的领域模型在不同上下文表达各自的知识语境,需要引入限界上下文,来确定业务能力的自治边界,并考虑通过持续集成来维护模型的统一。上下文映射清晰地表达了多个限界上下文之间的协作关系。根据协作方式的不同,可以将上下文映射分为如下8种模式:
-
客户方/供应方;
-
共享内核;
-
遵奉者;
-
分离方式;
-
开放主机服务;
-
发布语言;
-
防腐层;
-
大泥球
领域驱动设计元模型
模型驱动设计可以在限界上下文的边界内部进行,它通过分层架构(layeredarchitecture)将领域独立出来,并在统一语言的指导下,通过与领域专家的协作获得领域模型。表示领域模型的设计要素包括实体(entity)、值对象(valueobject)、领域服务(domainservice)和领域事件(domainevent)。领域逻辑都应该封装在这些对象中。这一严格的设计原则可以免领域逻辑泄露到领域层之外,导致技术实现与领域逻辑的混淆。
领域驱动设计概览
聚合(aggregate)(参见第15章)是一种边界,它可以封装一到多个实体与值对象,并维持该边界范围之内的业务完整性。聚合至少包含一个实体,且只有实体才能作为聚合根(aggregateroot)。工厂(factory)和资源库(repository)(参见第17章)负责管理聚合的生命周期。前者负责聚合的创建,用于封装复杂或者可能变化的创建逻辑;后者负责从存放资源的位置(数据库、内存或者其他Web资源)获取、添加、删除或者修改聚合。