微服务领域驱动设计 - semaphoreci


微服务是开发软件的最具可扩展性的方式。但是你需要一个好的设计,让开发团队自主工作和部署,而不会互相干扰,否则你将失去可扩展性的好处。
领域驱动开发允许我们通过将更大的系统分解为独立的单元、了解每个单元的职责并确定它们之间的关系来规划微服务架构。在本文中,我们将学习领域驱动设计的基础知识以及如何将其应用于微服务。

什么是领域驱动设计?
领域驱动设计 (DDD) 是一种软件设计方法,开发人员在其中构建模型以了解领域的业务需求。这些模型作为开发软件的概念基础。
根据域驱动设计的作者 Eric Evans 的说法:解决软件核心的复杂性,领域是:
知识、影响或活动的领域。用户应用程序的主题领域是软件的领域。

一个人解决问题的能力取决于一个人对该领域的理解能力。开发人员是聪明人,但他们不可能是所有领域的专家。他们需要与领域专家合作,以确保代码符合业务规则和客户需求。

上图说明:开发人员和领域专家使用统一的语言来共享知识、文档、计划和代码。

微服务架构的两个最重要的 DDD 概念是:有界上下文上下文映射

限界/有界上下文 (BC)
单词出现的环境决定了它的含义。根据上下文,“书”可能指的是书面作品,也可能意味着“预订房间”。有界上下文(BC)是一个术语具有明确和明确含义的空间。
在 DDD 之前,通常的做法是尝试找到一个跨越整个领域的模型。问题是域越大,就越难找到一致统一的模型。DDD 的解决方案是识别 BC,以便将域分解为可管理的子域。

在软件中,我们需要精确。这就是定义 BC 至关重要的原因:它为我们提供了一个精确的词汇表,称为通用语言,可用于开发人员和领域专家之间的对话。无处不在的语言存在于整个设计过程、项目文档和代码中。

上下文映射
BC 的存在预示着对通信渠道的需求。例如,如果我们在电子商务领域工作,销售人员应该在销售产品之前检查库存。一旦售出,就需要通过运输来确保将产品送到正确的地址。在 DDD 中,这些关系以上下文映射的形式描述。

微服务领域驱动设计
DDD 分两个阶段进行:

  1. 战略阶段,我们确定 BC 并将它们映射到上下文地图中。
  2. 战术阶段,我们根据子域的业务规则对每个 BC 进行建模。

让我们看看每个阶段如何在微服务架构设计中发挥作用。

1、战略阶段
在此阶段,我们邀请开发人员、领域专家、产品所有者和业务分析师进行头脑风暴、分享知识并制定初步计划。在主持人的帮助下,这可以采取事件风暴研讨会的形式,我们在其中构建模型并从领域中的重大事件开始识别业务需求。

在战略 DDD 中,我们采用高层次、自上而下的设计方法。我们首先分析域以确定其业务规则。由此,我们得出一个 BC 列表。

边界充当天然屏障,保护内部模型。因此,每个 BC 都代表了一个实现至少一个微服务的机会。

接下来,我们必须决定 BC 将如何通信。Eric Evans列出了七种类型的关系,而其他作者列出了其中的六种。不管我们如何计算它们,至少三个(共享内核、客户/供应商和遵从者)意味着紧密耦合,这是我们在微服务设计中不想要的,可以忽略不计。这给我们留下了四种类型的关系:

  • 开放主机服务(OHS):服务提供者定义一个开放协议供其他人使用。这是一种开放式的关系,因为消费者需要遵守协议。
  • 已发布语言(PL):此关系使用众所周知的语言,例如 XML、JSON、GraphQL 或任何其他适合该领域的语言。这种类型的关系可以与 OHS 结合使用。
  • 反腐败层(ACL):这是服务消费者的防御机制。反腐败层是在下游服务之前实现的抽象和翻译包装层。当上游发生变化时,消费者服务只需要更新 ACL。
  • 不同的方式:当进一步分析发现两个服务之间的集成没有什么价值时,就会发生这种情况。这与关系相反——这意味着 BC 没有联系,也不需要交互。

在我们的战略 DDD 分析结束时,我们得到一个详细描述 BC 及其关系的上下文图。

战术阶段
在内心深处,软件开发是一种建模练习;我们将现实生活中的场景描述为模型,然后用代码解决它。在上一阶段,我们确定了 BC 并绘制了它们的关系。在这个阶段,需要开发人员精通 DDD 理论,我们将放大每个上下文以构建详细的模型。
使用 DDD 创建的模型与技术无关——它们没有说明底层的堆栈。相反,我们专注于对子域进行建模。我们模型的主要构建块是:

  • 实体:实体是具有随时间持续存在的身份的对象。实体必须具有唯一标识符(例如,客户的帐号)。虽然实体标识符可以在上下文边界之间共享,但实体本身不需要在每个 BC 中都相同。每个上下文都可以拥有给定实体的私有版本。
  • 值对象:值对象是没有标识的不可变值。它们代表您模型的原语,例如日期、时间、坐标或货币。
  • 聚合:聚合创建实体和值对象之间的关系。它们代表一组可以被视为一个单元并且始终处于一致状态的对象。例如,客户下订单并拥有书籍,因此可以将实体 customer、order 和 book 视为一个聚合。聚合必须始终由称为根实体的主实体引用。
  • 领域服务:这些是实现业务逻辑或功能的无状态服务。域服务可以跨越多个实体。
  • 领域事件:对于微服务设计至关重要,领域事件会在发生某些事情时通知其他服务。例如,当客户购买一本书时,付款被拒绝,或者用户已经登录。微服务可以同时产生和消费来自网络的事件。
  • 存储库:存储库是聚合的持久容器,通常采用数据库的形式。
  • 工厂:工厂负责创建新的聚合。

领域驱动设计是迭代的
虽然看起来我们必须首先编写一个详尽的领域描述,然后才能开始处理代码,但现实情况是,DDD 与所有软件设计一样,是一个迭代过程。
在纸面上,限界上下文和上下文映射可能看起来不错,但在实施时,它们可能会转化为太大而无法正确称为微服务的服务。相反,具有重叠职责的聊天微服务可能需要合并为一个。
随着开发的进展,您对领域有了更好的理解,您将能够做出更好的判断、增强模型并更有效地进行沟通。

更多设计微服务的方法
DDD 无疑是一种重理论的设计模式。因此,仅当正在开发的系统足够复杂以保证额外的规划工作时才推荐使用。
其他方法,例如测试驱动开发(TDD) 或行为驱动开发(BDD),对于更小、更简单的系统可能就足够了。TDD 在处理单个微服务或什至仅由少数服务组成的应用程序时是最快的开始并且效果最好。

在更大的范围内,我们可以使用 BDD,它迫使我们通过集成和验收测试来验证批发行为。如果您从事低到中等复杂度的设计,BDD 可能会很好地工作,但是一旦您达到某个阈值,维护测试可能会减慢您的速度。

您还可以将这三种模式结合起来,为每个开发阶段选择最好的一种。例如:

  1. 识别微服务及其与战略 DDD 的关系。
  2. 使用战术 DDD 对每个微服务进行建模。
  3. 由于每个团队都是自治的,因此他们可以选择采用 BDD 或 TDD(或两者混合)来开发微服务或微服务集群。