DDD的相关概念

大家好,我是指北君。

最近在学习领域驱动编程,对,就是那个DDD。指北君主要的学习资料就是“极客时间”上的《DDD实战课》,这里,指北君将课程中一些重要的概念摘了出来,因为要想了解DDD,其概念是基础,非常重要。 当大家了解这些概念后,会对DDD有个初步浅显的认识,如果想继续深入了解,就需要大家去翻看专业书籍,或者去极客时间上看《DDD实战课》。好了,下面我们开始正题吧


一. 什么是DDD

DDD 是一种处理高度复杂领域的设计思想,它试图分离技术实现的复杂性,并围绕业务概念构建领域模型来控制业务的复杂性,以解决软件难以理解,难以演进的问题

有些熟悉 DDD 设计方法的软件工程师在进行微服务设计时,发现可以利用 DDD 设计方法来建立领域模型,划分领域边界,再根据这些领域边界从业务视角来划分微服务边界

DDD 包括战略设计和战术设计两部分:

  • 战略设计:主要从业务视角出发,建立业务领域模型,划分领域边界,建立通用语言的限界上下文,限界上下文可以作为微服务设计的参考边界
  • 战术设计:则从技术视角出发,侧重于领域模型的技术实现,完成软件开发和落地,包括:聚合根、实体、值对象、领域服务、应用服务和资源库等代码逻辑的设计和实现。

一般具体的设计步骤:

  1. DDD 领域建模通常采用事件风暴,它通常采用用例分析、场景分析和用户旅程分析等方法,通过头脑风暴列出所有可能的业务行为和事件,
  2. 然后找出产生这些行为的领域对象
  3. 梳理领域对象之间的关系,找出聚合根
  4. 找出与聚合根业务紧密关联的实体和值对象
  5. 将聚合根、实体和值对象组合,构建聚合

二. 领域

在研究和解决业务问题时,DDD 会按照一定的规则将业务领域进行细分,领域细分到一定的程度后,DDD 会将问题范围限定在特定的边界内,并在这个边界内建立领域模型,进而用代码实现该领域模型,解决相应的业务问题。

三. 子域、核心域、通用域、支撑域

领域可分解为子域,子域可继续分为子子域,一直到你认为适合建立领域模型为止。

DDD 的子域分为核心域、通用域和支撑域。划分这几个子域的主要目的是为了确定战略资源的投入,一般来说战略投入的重点是核心域,

核心域:决定业务成功的主要因素和公司的核心竞争力。

通用域:没有太多个性化的诉求,同时被多个子域使用的通用功能子域(通用系统,没有企业特点限制,不需要做太多的定制化。)

支撑域:不是必需的,(具有企业特性,但不具有通用性,例如数据代码类的数据字典等系统。)

在不同的场景下,不同的人对核心域的理解是不同的。公司在 IT 系统建设过程中,由于预算和资源有限,对不同类型的子域应有不同的关注度和资源投入策略,记住好钢要用在刀刃上。

四. 聚合

实体一般对应业务对象,它具有业务属性和业务行为,值对象主要是属性集合,对实体的状态和特征进行描述。他们只是个体化的对象,它们的行为表现出来的是个体的能力。而能让实体和值对象协同工作的组织就是聚合,它用来确保这些领域对象在实现共同的业务逻辑时,能保证数据的一致性。

五. 聚合根

聚合根的主要目的是为了避免由于复杂数据模型缺少统一的业务规则控制,而导致聚合、实体之间数据不一致性的问题。传统数据模型中的每一个实体都是对等的,如果任由实体进行无控制地调用和数据修改,很可能会导致实体之间数据逻辑的不一致。而如果采用锁的方式则会增加软件的复杂度,也会降低系统的性能。

如果把聚合比作组织,那聚合根就是这个组织的负责人。聚合根也称为根实体,它不仅是实体,还是聚合的管理者。首先它作为实体本身,拥有实体的属性和业务行为,实现自身的业务逻辑。其次它作为聚合的管理者,在聚合内部负责协调实体和值对象按照固定的业务规则协同完成共同的业务逻辑。最后在聚合之间,它还是聚合对外的接口人,以聚合根 ID 关联的方式接受外部任务和请求,在上下文内实现聚合之间的业务协同。也就是说,聚合之间通过聚合根 ID 关联引用,如果需要访问其它聚合的实体,就要先访问聚合根,再导航到聚合内部实体,外部对象不能直接访问聚合内实体。

六.实体和值对象

在战略设计向战术设计过渡的这个过程中,理解和区分实体和值对象在不同阶段的形态是很重要的,

实体:它们拥有唯一标识符,且标识符在历经各种状态变更后仍能保持一致的对象。

  • 业务形态:领域模型中的实体是多个属性、操作或行为的载体。在事件风暴中,我们可以根据命令、操作或者事件,找出产生这些行为的业务实体对象,进而按照一定的业务规则将依存度高和业务关联紧密的多个实体对象和值对象进行聚类,形成聚合。
  • 代码形态:实体在代码形态的表现形式是实体类,这个类包含了实体的属性和方法,通过这些方法实现实体自身的业务逻辑。在 DDD 里,这些实体类通常采用充血模型,与这个实体相关的所有业务逻辑都在实体类的方法中实现,跨多个实体的领域逻辑则在领域服务中实现。
  • 运行形态:实体以 DO(领域对象)的形式存在,每个实体对象都有唯一的 ID。我们可以对一个实体对象进行多次修改,修改后的数据和原来的数据可能会大不相同。但是,由于它们拥有相同的 ID,它们依然是同一个实体。比如商品是商品上下文的一个实体,通过唯一的商品 ID 来标识,不管这个商品的数据如何变化,商品的 ID 一直保持不变,它始终是同一个商品。
  • 数据库形态:一个实体可能对应 0 个、1个或者多个数据库持久化对象。

值对象:描述了领域中的一件东西,这个东西是不可变的,它将不同的相关属性组合成了一个概念整体。当度量和描述改变时,可以用另外一个值对象予以替换。它可以和其它值对象进行相等性比较,且不会对协作对象造成副作用

  • 业务形态:实体具有业务属性、业务行为和业务逻辑。而值对象只是若干个属性的集合,只有数据初始化操作和有限的不涉及修改数据的行为,基本不包含业务逻辑。
  • 代码形态:值对象在代码中有这样两种形态。如果值对象是单一属性,则直接定义为实体类的属性;如果值对象是属性集合,则把它设计为 Class 类,Class 将具有整体概念的多个属性归集到属性集合,这样的值对象没有 ID,会被实体整体引用。
  • 运行状态:值对象嵌入到实体的话,有这样两种不同的数据格式,也可以说是两种方式,分别是属性嵌入的方式和序列化大对象的方式。
  • 数据库形态:在领域建模时,我们可以把地址作为值对象,人员作为实体,这样就可以保留地址的业务涵义和概念完整性。而在数据建模时,我们可以将地址的属性值嵌入人员实体数据库表中,只创建人员数据库表。这样既可以兼顾业务含义和表达,又不增加数据库的复杂度。值对象就是通过这种方式,简化了数据库设计,

七. 领域事件

领域事件是领域模型中非常重要的一部分,用来表示领域中发生的事件。一个领域事件将导致进一步的业务操作,在实现业务解耦的同时,还有助于形成完整的业务闭环。如果发生某种事件后,会触发进一步的操作,那么这个事件很可能就是领域事件。

领域事件一般采用最终一致性,在领域模型映射到微服务系统架构时,领域事件可以解耦微服务,微服务之间的数据不必要求强一致性,而是基于事件的最终一致性。

八. 小结

好了,这就是DDD中最重要的几个概念,大家如果了解了,应该对DDD有了一个浅显的认识,但如果大家想在工作中应用DDD,还需要找相关课程或者资料深入的学习! 我是指北君,操千曲而后晓声,观千剑而后识器。感谢各位人才的:点赞、收藏和评论,我们下期更精彩!

Java Geek Tech wechat
欢迎订阅 Java 技术指北,这里分享关于 Java 的一切。