领域驱动设计:业务建模与架构实践
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.2 DDD的基本原则

本节将讨论DDD的基本原则,它是DDD所特有的,是其区别于其他方法论的本质,之后我们将分析为什么两个基本原则是前面两个事实得以实现的保证。

1.2.1 DDD的两个基本原则

DDD的基本原则是战略、战术和各种模式的基础。这些原则是DDD独特且不可或缺的要素,是使其成为一种革命性设计方法的根本原因。原则必须遵守,否则事物的独特性会消失,优点和收益也会消失。DDD的基本原则如下:

(1)保持语言、模型、代码三者一致

❑ 语言:开发团队与领域专家沟通使用的自然语言。因为它与设计模型、代码是一致的,所以也称为通用语言。

❑ 模型:设计的输出物,是对领域逻辑的精准建模。模型会充分体现具有领域含义的术语和关系。作为通用语言的核心,模型既充当着沟通业务逻辑的媒介,也直接指导开发实现。

❑ 代码:模型和通用语言在实现层的精确表达。

一致也可以理解为领域逻辑、设计、实现三者一致。一致性体现为三者的信息量是一致的,中间没有二次设计,无转译,无熵增。模型和代码(至少是测试代码)应该能被领域专家所阅读和理解。所有人通过模型来理解和交流领域逻辑。三者会相互影响,不断进化,但要实时保持一致。

一致性亦体现为语言传达的领域逻辑必须被显式、集中地体现在模型和代码中,而不能被隐式、模糊或分散地表达。

(2)保持领域模型的独立性和内聚性

❑ 独立性:领域模型可以被独立开发、测试和验收,与系统架构中的其他任何部分都没有依赖关系(只会被依赖)。

❑ 内聚性:关联紧密的业务逻辑要通过模型组织在一起,不宜分散。模型不包含除领域逻辑之外的其他内容,且领域模型是业务逻辑的唯一载体,一处业务逻辑只能存在于一个模型内。

上述两个原则是DDD的本质特征。遵守这两个原则,不论是否应用了DDD其他的战略战术模式,都是DDD设计方法。违反了上述原则,即便使用了DDD的某些战略战术模式,也不能算是纯正的DDD设计方法。

(3)相关的问题与解答

下面我们借助对一些问题的回答来把这两个原则弄清楚。

1)为什么语言、模型、代码三者一致是DDD的第一条原则,它们难道不是不同层面的事物吗?如何保持一致?

如果三者是不同层面的事物,那么就是我们前面举例的传统设计方法。从业务沟通到设计,从设计到代码,可能都由不同的人负责和二次设计,每一层都会带来额外的熵增。

三者一致的原则,是DDD最有价值的部分。为此,DDD引入了“通用语言”的概念。三者保持一致,领域才能起到“驱动”的作用,三者不一致,那么驱动的就不再是“领域”,而是另有他因了。所以这是DDD最本质的原则。

至于如何保持三者一致,离不开通用语言的打造,离不开代码的一些规范,本书后面的大部分内容都将围绕解决这个问题展开。

图1-3是三者一致性与传统方式的对比。可以看到,相对于传统开发模式,DDD语言和代码都统一于单一模型,模型是语言的核心,代码是模型的精确表达。

图1-3 三者一致性与传统方式的对比

2)通用语言究竟是什么?

通用语言承担着两个任务。第一个任务是沟通,涉及领域专家与用户之间、领域专家与技术团队之间、技术团队成员之间的沟通。在这种沟通中,模型是核心,用于沟通业务逻辑和需求,所有人都通过模型来理解和交流领域逻辑,包括领域专家自己,也要通过模型重塑对领域的理解。随着模型的丰富,语言也相应发生变化。第二个任务是指导代码的开发,因为三者是一致的,代码必须是语言与模型的表达。

通用语言不能完全是业务日常语言,因为业务日常语言可能不清晰,有歧义,且需要提炼,用来做模型设计和指导开发显然有不足之处。所以,通用语言应该是以消除了所有歧义之后的业务语言为基础,以领域模型为核心,由领域专家和技术团队共同打造的沟通媒介。

成功的通用语言标准很简单,即团队在所有的日常交流中(尤其是会议中),包括代码中都能自如地使用这种语言。

要“打造一种语言”,这个提法可能会让落地DDD的团队望而生畏,但其实大可不必担心。它并不是开发团队必须去学习的一门“外语”,而是团队与领域专家通过彼此磨合,以领域模型为核心,以需求为边界,不多不少的一个沟通媒介。本书第5章将提供详细的指导,帮助团队打通通用语言与模型。

3)怎么理解“领域模型保持独立性和内聚性”?

这也是DDD最有价值的部分之一。

独立性是我们追求的目标,即领域模型单独被构建,不再与技术复杂度混合在一起,不再与程序的其他关注点(如界面、持久化、集成、通信)混合在一起,从而可以单独被测试、验证和重用。技术上,可以采用六边形架构、依赖倒置的分层架构、资源库模式、接口和晚绑定等来实现(详见第10章)。

内聚性可以理解为“有且仅有”,“有”是说相关的领域逻辑要在一起,它可以在模型内,也可以体现在模型的关系中(聚合),但必须显式、集中地体现而不能隐含地散布在各处。同时也“仅有”相关逻辑,而不含其他不相关关注点的内容。

同时,领域模型是业务逻辑的唯一载体和调用入口,同一个逻辑只存在一个地方,而不论是设计还是代码。这是架构原则“语义一致性”的体现,也是我们能独立测试和验收领域逻辑的保证。(我们不可能测试了一个地方,但系统运行时起作用的却是另一个地方)。事实上,不只是DDD,所有架构设计方法都应该遵守这一原则。

如图1-4所示,领域模型犹如钢铁侠的能量块,是整个项目的核心,它能够被单独构建、验证和使用。

图1-4 系统的核心在独立的领域模型中

4)DDD的重点到底是两个基本原则、模型的构建技巧还是战略战术层面模式的运用?

要说什么是最重要的,必然是前面的两个原则,这是DDD区别于其他方法的身份证明,而不是模型的构建技巧与战略战术层面的各类模式。除了两个基本原则,其他的实践都可以在其他的方法论中看到,比如模型驱动设计、面向对象设计、事件驱动设计、微服务设计等。很多模式也非DDD所独享,比如工厂模式、领域服务、无副作用函数等。

当然,这也绝不是说除两大原则之外的战略战术层面的模式对DDD不重要。因为这些模式是保证原则落地的具体手段,我们必须在项目管理、团队文化、企业架构、代码规范等方面配套,才能保证三者一致,必须掌握上下文、子域、设计模式、六边形架构等技巧,才能保证独立于内聚。这些模式与原则是相辅相成的。

如前所言,Evans的著作中介绍了很多模式来保证原则。Vernon的著作《实现领域驱动设计》中也有大量相关技术和解决方案。DDD给这些相关技术提供了更好的舞台,在这些结局方案和技术的加持下,DDD也得以实现自己的目标,不会因为缺少必要的技术解决方案的支撑而成为空中楼阁。

5)图1-3中既沟通业务又指导实现的单一模型真的可以实现吗?

面向对象编程技术的成熟,使得这一点是完全可以做到的。事实上,当我们意识到要构建单一模型时,我们的DDD之旅就步上了正轨。本书第4~6章的核心内容讲授构建单一模型的各种技巧和方法,让其做到既清晰地表达业务,又能无碍地连接代码和实现。

6)DDD是一种设计思想还是一种软件过程?

DDD无疑是一种架构设计思想,它有基本原则和诸多战略战术设计模式,但没有规定自己独有的软件过程。根据强调领域专家和开发团队的协作沟通以及模型的持续演进,采用敏捷和迭代的软件过程是比较合适的,这一点在第2章中有阐述。

如果你对这些问题仍有疑问,不用担心,因为原则在整本书中都占有核心地位,后续章节将被反复提及,我们完全可以在多次的讨论中消除所有困惑。

1.2.2 原则的底层逻辑

前面提出了两个研发团队希望确认的事实,提到了DDD的两个基本原则。原则来源于对Evans著作中核心思想的提炼,而事实是我们对底层逻辑的思考和探究。两者之间的对应关系如表1-1所示。

表1-1 原则与事实的对应关系

我们的设计遵循语言、模型、代码三位一体的原则,保证了设计的复杂度就是业务的复杂度,不会更高。设计实现由“领域驱动”而不是其他因素驱动。

软件的设计和实现是领域工作的形式,简单地这么说并不严谨,因为有一个抽象提炼的过程。应该说,“设计是领域专家认可的领域工作方式的等价抽象”。领域专家在其中扮演着重要角色,“认可”意味着领域专家与开发团队共同打造模型,并在已有模型的基础上重新审视业务,更深入地投入设计和开发环节,确保模型在解决问题的维度上与业务本身一致。同时,开发人员需要放下纯技术思维,通过通用语言与领域模型塑造对领域逻辑的了解。

如图1-5所示,采用DDD设计的架构复杂度最低,因为设计本身“忠实”于业务,“忠实”于领域专家对模型的认可和反馈。它的复杂度不会超越业务人员对领域的理解,自然不会超过业务本身的复杂度。

图1-5 架构复杂度对比

图1-5中列出的其他架构方法架构,如微服务架构、分层架构和大泥球架构,单纯地使用它们,复杂度都会超过DDD架构,但如果能结合两者,也可以达到复杂度最简的效果,这一点会在第10章加以叙述。从这里可以看出,DDD更贴近本质的特性,也能给其他设计方法赋能。比如,按上下文和子域划分微服务,依此划分的微服务架构复杂度也不高。

有没有可能,我们的设计复杂度比业务复杂度还低呢?前面已经讨论过,这是个伪命题。在我们使用DDD方法的过程中,这种情况不是不可能发生。一方面,通用语言是去除歧义的语言,团队在打造通用语言的过程中,澄清了之前业务模糊的地方。另一方面,随着领域专家通过模型重塑对领域的理解,他们可能会迸发出对业务逻辑优化的想法。这可能是采用DDD的团队的意外收获。但严格来说,本质都是先优化了业务,再谈构建的系统。当然系统使业务得到了优化,最终业务、设计、实现也得到了统一。

再来看“保持领域模型的独立性和内聚性”原则,它确认了第二个事实——我们能单独构建、测试和验收业务逻辑,而不依赖于系统架构的其他部分。

如图1-6所示,所有的业务逻辑都封装在领域模型中,独立于技术架构开发领域模型,构建领域层,是完全可以实现的。比较常见的解耦方式是使用六边形或多层架构,多层架构已被大部分软件系统所采用,我们只需稍做一下改造即可适配DDD(详见10.3.2节)。这样,领域驱动设计的关注点就只有领域层,专心聚焦于构建强壮而独立的领域层,将是项目成功的坚实保障。

如果领域逻辑与程序中的其他关注点混在一起,就不可能拥有自身的独立性和内聚性。这不仅难以维护,而且无法利用单独测试验证和脱离系统重用的好处。

后面我们会讲到,一个独立的领域模型不仅使团队专注于领域设计,专心攻克业务逻辑这个项目成败的关键,更重要的是我们得到了一个强大的业务模型,它体现的其实是企业独特的商业逻辑和盈利模式,将这种核心竞争力数字化,无疑会给企业带来额外的好处。

综上所述,语言、模型和代码三者一致可以以最低复杂度来设计和实现软件。领域模型的独立性和内聚性可以确保我们单独构建、测试和重用领域逻辑,而不必考虑其他因素。

图1-6 领域模型是系统中的核心

理解这些关系并不难。DDD就是我们在1.2.1节提出的问题的答案。这个底层逻辑证明了DDD的价值。当然,对于习惯于传统分析方式的读者,我们仍然可以从大家熟悉的软件工程角度给出DDD的价值,即复杂度控制、架构原则和团队协作三个方面。这为后续章节的展开打下了更为坚实的基础。