DDD:领域驱动设计&微服务

587 阅读11分钟

领域驱动设计(DDD)概念由第一位Eric Evans于2003年提出。微服务的概念当时还不存在。因此,基本上引入了DDD来解决大型整体代码库的问题。在单体世界中,一旦代码库随着业务的增长而开始增长,就很难维护其最初设计的组织和结构化的代码。使用MVC架构设计的单体应用程序在业务层和表示层之间具有良好的分隔。但是在缺乏严格的体系结构准则的情况下,业务层不提供特定的规则来维护不同模块和类之间的责任界限。这就是为什么随着代码库的增长,它会增加逻辑崩溃的风险,

DDD试图通过使您的应用程序靠近REAL-WORLD系统或更准确地说与相关企业联系来解决上述挑战。在DDD中,应用程序逻辑围绕具有定义边界上下文的业务问题展开。DDD专注于领域建模。让我们讨论一下DDD的关键特性。

  1. 协作:DDD是协作的。业务实体,利益相关者和开发人员共同努力解决特定的业务问题。它更像是一种以协作为关键的敏捷方法。
  2. 数据建模:数据建模定义代码的结构应映射到领域的结构。在这里,领域不被称为业务,这可以被称为业务的一部分。该领域可以是会计,店面,仓库等。将代码模型映射到该领域的背后思想是:使业务用户可以理解应用程序的高层结构。因此,当业务扩展并且业务的某个领域需要对应用程序进行某些增强时,您可以更改与该领域相关联的模型和逻辑,并使应用程序的另一部分保持不变。
  3. 增量式:DDD是增量式的,因此您不需要预先设计整个业务架构。您只需要解决当前问题,然后随着领域或业务的增长而发展代码。DDD再次与AGILE方法学保持一致,并具有这种增量方法的特征。增量方法是一个过程,即快速增量地发布新功能。

什么是微服务?

简而言之,微服务就是要解决一个特定的问题。在Monolith设计中,我们拥有庞大的代码库,该代码库是数据驱动的并且紧密耦合。因此,每当我们需要更改数据模型时,整个整体设计都会受到影响。甚至增量方法也非常麻烦,因为整个应用程序必须在所有情况和场景下进行测试,并进行不同的依赖性检查,以确保更新不会引起任何故障。微服务架构解决了单体设计的这一问题,并提供了向任意方向扩展应用程序的自由。

DDD与微服务有何关系?

微服务可能是实现DDD的理想方法,因为在微服务中,我们还讨论了上下文和增量方法,这也是DDD的主要特征。DDD和微服务都围绕业务概念建模。微服务的一些基本特征如下:

  1. 小型:微服务应足够小以解决单个问题。它可以与OOPS术语中的类一样小,也可以与“功能设计”术语中的函数一样小。
  2. 可独立部署:微服务可独立部署。因此,基本上,您无需像添加完整功能的整体那样更新整个应用程序。您只需更改相关的微服务并检查其上下文边界即可开始部署。
  3. 分离:应该将各个微服务分离,隔离,并向世界隐藏其实现细节。应该仅通过服务访问微服务以提供解耦。

DDD的关键构建块

  1. 领域:这是定义不同业务实体的逻辑区域。例如,运输业务可能具有库存领域,会计领域,跟踪领域等。
  2. 有界上下文:这是代码中的逻辑边界,可以解决特定领域的问题。它可以是单个类,也可以是编码为解决特定问题的文件组。不同的有界上下文是彼此完全隔离的,并且彼此之间并不存在。定义上下文的最简单方法是定义领域的“职责”。例如,一个电子商务公司可以有两个域,分别名为“仓库”和“商店”。“仓库”负责“发货”,而“商店”负责“销售”,因此可以将它们视为有限的上下文。
  3. 服务:服务是不同有界上下文之间的通信渠道。您需要服务来使不同的有界上下文保持去耦,但仍可通信。

  1. 值对象:值对象没有独特的身份,并且是不可变的。它由其属性定义。例如,名称,地址等是值对象。
  2. 实体:实体是领域对象。实体仅在有限的上下文中执行特定的操作。这些由唯一身份标识。实体的一些示例可以是用户,订单,作业,消息等。实体是可变的,并且可以更改其身份。例如,一个订单可能会将其状态从已订购更改为已发货,并且有一个唯一身份标识“ order_id”要识别。
  3. 无所不在的语言:这表示上下文中特定于语言的语言。例如,如果您在“商店”上下文中谈论对象“书”,那么您将更加关注与“出售产品”相关的属性,例如价格,作者等,而在“仓库”中的对象是“书” ”上下文应与重量,尺寸等与“运送产品”相关的属性更多相关。因此,很明显,同一对象在不同上下文中可能具有不同的会话语言。无处不在的语言不仅取决于上面定义的NOUN级别或属性级别,而且还与VERB级别相关,或者用简单的话来说,与您对这些对象的处理方式有关。例如,“书”对象应用于在“商店”上下文中“出售或整理”书籍,而应在“仓库”上下文中用于“装箱”书籍。

DDD如何实施?

DDD方法可用于分析领域以及开发代码,此过程称为“事件风暴”。我们讨论领域,并确定在“事件风暴”期间在领域级别可能发生的事件。请记住,它是协作的和增量的,因此您必须让企业和开发人员都参与其中,以定义事件并围绕它进行建模。让我们以电子商务业务为例来确定DDD方法。请注意,这不是一个完整的示例。我仅以此为参考。在采用DDD方法时,我们应关注三个W,即“ WHEN”,“ WHAT”和“ WHO”。 您应该首先定义业务领域及其职责来开始DDD方法。电子商务公司可能具有不同的领域,例如“仓库”,“商店”等。下一步是定义这些领域的职责。例如,“仓库”负责运送书籍,而“商店”领域负责出售书籍。这两个领域都有各自的局限性,当然不必干涉彼此的工作或了解彼此的工作风格。因此,在这里我们可以分离上下文,并使单个上下文及其实现对外界隐藏。

现在,我们需要确定与领域相关的事件。在“仓库”和“商店”上下文中,可以是“下订单”,“生成运输标签”,“已下订单”等。这些事件可以手动驱动也可以自动驱动。在DDD中,这是一个重要的概念,我们不在乎演员或个人,我们在乎ROLE及其相关事件。例如,如果用户登录并创建发票,则我们不在乎该个人用户,而是在关注用户的角色(即“帐户”)和用户执行的事件“创建发票”。基本上,在派生事件时,我们定义领域的“何时”部分。

确定事件后,我们将确定“操作”。动作由事件触发。例如,“订单已启动”事件可能会触发企业中的“检查库存,启动付款流程和开具发票”操作。通过定义动作,我们定义了与事件相对应的“事件”。我们只需要简单地将事件和动作相关联以创建一个流程,并以“ X”结束该流程,您会感觉到流程结束了,并且无需进一步操作。

确定事件和动作后,我们需要确定角色以定义“ WHO”将执行该动作或由谁来响应这些事件。“世界卫生组织”的这一定义对于得出界限以及与行动有关的工作很重要。在不同的上下文中,可能有与同一对象关联的不同动作。例如,Order实体对于“商店”和“仓库”上下文而言意味着不同的东西。新订单意味着创建发票并根据商店上下文生成运输标签。而新订单意味着要运送产品在仓库环境中。因此,我们必须定义“ WHO”将处理该对象,事件或操作。通过此操作,您甚至可以确定有多少个流程或操作与人力相关,以及有多少个操作可以自动化。

编排

有界上下文可以通过服务进行通信。可以在两个模型中完成不同有界上下文的编排,即声明性模型和反应性模型。

陈述式

—顾名思义,该模型通过声明需要由不同实体执行的工作来工作。一个实体告诉其他实体该怎么做。自上而下的方法具有自然的声明流程,可在不同实体之间建立紧密的关系。这种紧密的关系通常会导致一个固有的维护问题,即如果您更改任何下游实体,那么所有上游上下文都必须检查并在需要时进行更改。例如,一旦下订单,它就可以调用服务或方法或其他微服务来“创建运输标签”和“生成收货”。现在,如果要更改“生成收货”,则还需要检查“订单实体”,以确保“生成收货”实体中的更改不会影响“订单实体”。

反应式

-在反应式模型中,没有两个处于不同有界上下文中的实体彼此了解实现细节。它们是完全隔离和彼此分离的,并且不知道世界上其他人的存在。它们通过PUB-SUB模型进行通信,其中“订单”实体发布事件“已下订单”,而订阅该事件的所有其他实体均旨在执行定义的工作。在反应模型中,如果您更改“生成收货”实体,则无需更改“订单实体”中的任何内容,因为发布的事件仍是“已下订单”,并且只要“生成收货”实体可以响应该事件然后执行分配的任务,那么一切正常。

实现真正的微服务架构的正确方法是通过Reactive Model。市场上有几种服务,例如Kafka,RabbitMQ,ZeroMQ,AQS SQS等,可用于创建这些事件队列以实现分离的体系结构。

领域驱动设计的优势

  1. 有效的沟通-使用通用语言有助于开发人员,业务用户和利益相关者之间更好的协作。每个人都可以在定义的上下文中以相同的语言谈论对象。
  2. 有效控制-DDD方法可帮助企业所有者控制技术的实施,同时由于通用的通用语言而对技术一无所知。
  3. 增量式方法— DDD使业务用户和开发人员可以专注于当前问题并提供快速的增量式更新,而不必等待大量发布日期。
  4. 灵活-之所以灵活,是因为对有约束的上下文进行了强大的封装,这使采用和交付更改的痛苦减轻了。

领域驱动设计的缺点

  1. 高初始投资-DDD方法最初需要大量投资才能得出DDD图。它还需要业务用户的积极参与,这也增加了产品的投资。
  2. 业务用户可用性—它要求业务用户具有连续可用性,以派生内容中使用的正确语言。它还要求开发人员在启动代码之前先学习领域。

但是总的来说,我认为DDD与微服务一起可能是设计中型或大型应用程序的好方法,以使业务和开发人员都积极参与每次增量更新,但这可能是对小规模产品的全面检查。