本文是我们关于领域驱动设计 (DDD) 的深入系列文章的第一部分。第二部分讨论战术 DDD,而在第三部分中,您将学习如何使用 Java 和 Vaadin 将 DDD 应用于工作软件。2023 年更新。

领域驱动设计 (DDD) 自 Eric Evans 于 2003 年出版有关该主题的书以来就一直存在。  
几年前,当我加入一个遭受数据一致性问题困扰的项目时,我自己就接触到了 DDD。数据库中出现了重复项,一些信息根本没有保存,您可以随时随地遇到乐观的锁定错误。
我们通过使用战术领域驱动设计的构建块设法解决了这个问题。

从那时起,我学到了更多关于领域驱动设计的知识,并尝试在我的项目中适当地使用它。
然而,在过去的几年里,当我与其他开发人员交谈时,他们中的许多人都听说过领域驱动设计这个词,但他们不知道它的含义。

在本系列文章中,我将简要介绍我所看到和理解的领域驱动设计。内容很大程度上基于Eric Evans的**《Domain-Driven Design: Tackling Complexity in the Heart of Software** 》和Vaughn Vernon的 《Implementing Domain-Driven Design 》这两 本书 。然而,我试图用我自己的话来解释一切,也注入了我自己的想法、观点和经验。

通过阅读我的系列文章,你不会成为领域驱动设计的专家,但我希望它能激励你在其他地方阅读更多关于它的信息。我也强烈建议你阅读埃文斯和弗农的书。

现在让我们从第一个主题开始,战略领域驱动设计。

 什么是域名?

如果我在 MacBook 的 Dictionary 应用程序中查找 单词 domain ,我会得到以下定义:

[A]由特定统治者或政府拥有或控制的领土区域……

  • 指定的活动或知识领域……

 — 苹果词典

在领域驱动设计的情况下,我们感兴趣的是定义的第二部分。在这种情况下, 活动 是组织所做的任何事情,而 知识 是组织如何做。我们还将把组织开展活动_的环境_ 添加到 领域概念中。

 子域

领域概念非常广泛和抽象。为了使其更加具体和有形,将其拆分为称为子域的较小部分是有意义的。
找到这些子域并不总是那么容易,如果你弄错了它们,当你的拼图中的各个部分突然不能很好地组合在一起时,你可能会遇到麻烦。

在寻找子域之前,您应该考虑子域类别。所有子域都可以分为三类:

  1.  核心域

  2.  支持子域

  3.  通用子域

这些类别不仅可以帮助您找到子域,还可以帮助您确定开发工作的优先级。

 核心域使组织独一无二,与其他组织不同。一个组织在其核心领域没有特别的出色表现,就无法成功(甚至无法存在)。因为核心域如此重要,所以它应该得到最高的优先级、最大的努力和最好的开发人员。
您只能为较小的域标识单个核心域,而较大的域可能具有多个核心域。您应该准备好从头开始实现核心域的功能。

 支持子域 是组织成功所必需的子域,但它不属于核心域类别。它也不是通用的,因为它仍然需要相关组织一定程度的专业化。
您可以从现有解决方案开始,然后对其进行调整或扩展以满足您的特定需求。

 通用子域 是一个子域,它不包含组织的任何特殊内容,但整体解决方案仍然需要它来运行。通过尝试为您的通用子域使用现成的软件,您可以节省大量时间和工作。
一个典型的例子是用户身份管理。

值得注意的是,根据组织的工作,相同的子域可以分为不同的类别。对于专门从事身份管理的公司来说,身份管理是一个核心领域。
但是,对于专门从事客户关系管理的公司来说,身份管理是一个通用的子域。

最后,值得指出的是,无论它们属于哪个类别,所有子域对于整体解决方案都是必不可少的。但是,它们确实需要不同的努力,并且可能对质量和完整性有不同的要求。

 例

假设我们正在为较小的诊所构建 EMR(电子病历)系统。我们确定了以下子域:

  1. 患者记录 ,用于管理患者医疗记录(个人信息、病史等)。

  2.  用于订购实验室测试和管理测试结果的实验室。

  3. 用于安排约会的日程安排。 

  4. File Archive 用于存储和管理附加到患者记录的文件(例如不同的文档、X 射线图片和扫描的纸质文档)。

  5. 身份管理 ,用于确保正确的人员能够访问正确的信息。

现在,我们如何对这些子域进行分类?最明显的是 文件存档 和 身份管理, 它们显然是通用的子域。但是其他人呢?这取决于是什么使这种特定的 EMR 系统在市场上的其他系统中脱颖而出。

Example subdomains

构建EMR系统的战略领域驱动设计(DDD)

在开发电子病历(EMR)系统时,领域驱动设计(DDD)提供了一种系统化的方法来处理复杂性并确保软件能够解决实际业务问题。以下是对DDD核心概念的梳理和应用示例。

核心域与支持子域

首先,识别系统中的核心域至关重要。例如,在EMR系统中,患者记录是核心域,因为它是系统提供价值的基础。如果系统的竞争优势在于创新的调度功能,那么调度也可能成为核心域。否则,它可能仅是一个支持子域,利用现有的调度引擎进行开发。

从问题到解决方案

领域也被看作是“问题域”,因为它定义了软件需要解决的业务问题。Vaughn Vernon将领域分为问题空间和解决方案空间。问题空间集中于业务问题,而解决方案空间则关注这些问题的具体技术实现。

无处不在的语言

为了创建领域的软件,需要一种描述领域的方式,这就是所谓的无处不在的语言。这种语言由领域专家和开发人员共同使用,是DDD过程中除代码外最重要的产出。通过创建术语表、泳道图、流程图等,可以记录这种语言。

有界上下文

由于业务流程可能存在重叠或冲突,我们引入了有界上下文的概念。有界上下文是领域中一个明确的部分,其中使用的语言是一致的。例如,在EMR系统的患者记录核心域中,可能存在如下有界上下文:

  1. 个人信息:管理患者的个人资料,如姓名、地址、财务信息等。2. 入职流程:新患者进入系统的初始流程。3. 医疗检查:医生用于检查和治疗患者的流程。4. 物理治疗:物理治疗师用于检查和治疗患者的流程。

边界上下文之间的关系

在复杂的系统中,有界上下文之间通常存在某种关系,可以是上游或下游关系。例如,一个有界上下文的变化可能会对下游的上下文产生影响。了解这些关系对于系统间的技术通信和团队间的协作至关重要。

整合模式

一旦明确了上下文及其关系,就需要决定如何整合它们。整合模式如合作伙伴关系、共享内核、客户-供应商、顺从者、反腐层、开放主机服务和发布语言等,提供了不同情境下的整合策略。

战略DDD的重要性

战略DDD不仅适用于大型项目,小型项目也能从中受益。它帮助我们划定界限,避免范围蔓延,同时让我们专注于最有价值的核心域。

结语

在下一部分,我们将探讨战术DDD,了解如何将有界上下文转化为可实施的设计,并进一步构建领域模型和无处不在的语言。 Example bounded contexts

有界上下文及其关系

在一个复杂的系统中,有界上下文(Bounded Contexts)之间的相互独立性是非常罕见的。大多数情况下,这些上下文会以某种方式相互关联。理解这些关系对于系统的技术实现以及开发团队之间的协作都至关重要。

有界上下文的分类

识别有界上下文之间关系的一个有效方法是将其分为上游上下文下游上下文。可以想象,这些上下文就像河流旁的城市。上游城市将物品倾倒入河中,而下游城市则从河中获取所需物品。一些物品对下游城市至关重要,而其他一些则可能带来损害。

上游上下文的优势与劣势

  • 优势:上游上下文不依赖于其他上下文,因此可以自由发展。- 劣势:变化可能对下游上下文产生严重影响,从而对上游上下文的发展造成限制。

下游上下文的优势与劣势

  • 优势:下游上下文不受其他下游上下文的影响,开发人员可以更自由地进行开发。- 劣势:受限于对上游上下文的依赖,必须考虑上游变化对自身系统的影响。

描述上下文关系的方法

上下文之间的关系可以通过依赖图来图形化表示,箭头从下游指向上游,或者使用U(上游)和D(下游)的角色来标识。

核心子域中的有界上下文示例

以电子病历系统(EMR)为例,我们可以识别出以下有界上下文:

  1. 患者个人信息管理:负责管理患者的姓名、地址、财务信息和医疗背景等。2. 新患者入职培训:将新患者引入系统并进行初始评估。3. 医生体检:医生在检查和治疗患者时使用的流程。4. 物理疗法:物理治疗师在检查和治疗患者时使用的流程。 尽管这些上下文位于同一核心子域内,但EMR系统通过为每种服务提供定制化的功能,实现了优化和简化。

结论

在设计系统时,理解并正确处理有界上下文之间的关系对于确保系统的灵活性、可维护性和长期成功至关重要。 Different ways of documenting context relationships graphically

上下文映射和集成模式

在战略领域驱动设计(DDD)中,上下文映射和集成模式是关键概念。它们帮助我们理解不同上下文之间的关系,并指导我们如何将它们有效整合。以下是整合上下文时需要考虑的几个重要问题:

  1. 确定上下文边界:明确每个上下文的范围和责任,以确保它们在解决特定问题时保持专注和一致。
  2. 技术交流方式:定义上下文之间如何进行数据和信息的交换,这可能涉及到API、消息队列或其他技术手段。
  3. 领域模型映射:在不同的上下文中,相同的概念可能有不同的表现形式。需要确定如何将一个上下文的领域模型转换为另一个上下文的模型。
  4. 防止上游变化影响:设计机制以减少上游变化对下游上下文的负面影响,例如通过版本控制或使用抽象层。
  5. 避免给下游带来问题:确保上游的变化不会破坏下游上下文的稳定性和预期行为。

上下文图的构建

上下文图是一种图形化表示上下文及其关系的工具。它可以帮助团队理解每个上下文的角色和它们之间的依赖关系。构建上下文图通常包括以下步骤:

  • 识别所有相关的上下文。- 确定每个上下文的边界和责任。- 描述上下文之间的交流方式和依赖关系。- 应用适当的集成模式来指导上下文之间的整合。

集成模式

  • 合作伙伴:上下文团队之间进行合作,共同发展接口以满足彼此的需求。- 共享内核:上下文共享一个代码库,任何修改都需要双方团队的协商。- 客户-供应商:定义上游为供应商,下游为顾客,供应商需要考虑顾客的需求。- 顺从者:下游团队接受上游团队的模型,不进行任何修改。- 反腐层:下游团队创建一个抽象层,以保护自己不受上游变化的影响。- 开放主机服务:通过定义清晰的服务和协议,允许任何需要的系统进行集成。- 发布语言:使用一种公开的、标准化的语言进行系统间的通信。- 分道扬镳:当整合的成本大于收益时,选择让上下文独立发展。 通过上述步骤和模式,我们可以构建一个清晰、有效的上下文图,指导我们的系统设计和开发。 An example context map

领域驱动设计(DDD)战略篇概述

领域驱动设计简介

领域驱动设计(DDD)是一种软件设计方法,由Eric Evans在2003年提出。它帮助解决软件项目中的数据一致性问题,如数据库中的重复数据、信息未保存以及乐观锁错误等。通过战术DDD的构建块,可以在项目中实现解决这些问题。

领域与子域

领域是组织活动和知识的特定领域。子域是领域的更小部分,有助于具体化和操作化领域概念。子域分为三类:

  1. 核心领域:使组织独特且无法或缺的部分。2. 支持子域:对成功至关重要但不属于核心的部分。3. 通用子域:对整体解决方案有用但无特殊性的领域。

从问题到解决方案

领域定义了软件要解决的问题。Vaughn Vernon将领域分为问题空间和解决方案空间。问题空间关注业务问题,而解决方案空间关注如何解决这些问题。

通用语言

通用语言是领域专家和开发者共同使用的描述和讨论领域的语言。它是DDD过程中除了代码之外最重要的产出。通用语言的体现是领域模型,最终将转化为工作代码。

有界上下文

有界上下文是领域内一个明确的部分,其中通用语言的特定子集始终保持一致。它是通过划分领域模型为更小的、具有明确边界的独立模型来实现的。

上下文关系

在复杂系统中,上下文之间通常存在某种关系。可以通过将上下文分类为上游和下游来识别这些关系。

上下文映射和集成模式

确定上下文及其关系后,需要决定如何整合它们。这涉及到上下文边界、技术通信方式、领域模型映射、防止上游问题变化和避免对下游上下文造成麻烦等问题。

合作伙伴关系

共享内核

客户-供应商

墨守成规

反腐败层

开放主机服务

发布语言

分道扬镳

战略领域驱动设计的重要性

战略DDD为大型项目设计,但小型项目也能受益。它引入了边界概念,帮助项目避免范围蔓延,专注于核心领域,从而实现最大价值。

下一步:战术领域驱动设计

在系列的第二部分,将探讨战术DDD,学习如何将有界上下文转化为可实现的设计。

Vaadin新用户体验

Vaadin新手可以通过start.vaadin.com配置并下载项目,创建自己的Vaadin应用程序。