侧边栏壁纸
博主头像
码森林博主等级

一起走进码森林,享受编程的乐趣,发现科技的魅力,创造智能的未来!

  • 累计撰写 146 篇文章
  • 累计创建 74 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录
DDD

DDD 领域驱动设计的核心概念

码森林
2024-03-18 / 0 评论 / 0 点赞 / 67 阅读 / 3,270 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2024-03-18,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

DDD 是什么?

DDD

Eric Evans 的“Domain-Driven Design领域驱动设计”简称DDD,Evans DDD是一套综合软件系统分析和设计的面向对象建模方法。

DDD最大的好处是:接触到需求第一步就是考虑领域模型,而不是将其切割成数据和行为,然后数据用数据库实现,行为使用服务实现,最后造成需求的首肢分离。DDD让你首先考虑的是业务语言,而不是数据。重点不同导致编程世界观不同。

核心思想

高内聚、低耦合,分而治之

什么是高内聚?

高内聚是指一个软件模块是由相关性很强的代码组成,只负责一项任务,也就是常说的单一责任原则。模块的内聚反映模块内部联系的紧密程度。

好处:可靠性、可读性、可重用性、可维护性

什么是低耦合?

模块与模块之间、系统与系统之间的交互,是不可避免的, 但是我们要尽量减少由于交互引起的单个模块无法独立使用或者无法移植的情况发生, 尽可能多的单独提供接口用于对外操作, 这个就是所谓的“低耦合”,耦合指的是模块之间存在依赖,关系越紧密, 耦合越强, 模块独立性越差。

好处:降低复杂度、提高可用性、提高可扩展性

DDD 的核心概念

领域(Domain)

领域是指一种特定的范围或区域,用来确定范围,范围即边界。

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

核心子域:在企业内决定产品或企业核心竞争力的功能子域,其让企业业务和商业模式成功的关键核心能力。

通用子域:没有太多个性化诉求,同时被对个子域重复使用的通用功能子域。

支撑子域:企业必需的,但它既不决定产品或企业核心竞争力的功能,也不被其他子域复用的通用功能。

这要考虑是企业应该在那个领域进行资源投入和建设策略。

通用语言(Ubiquitous Language)

在 DDD 领域建模和微服务建设过程中,会有很多项目参数者,如领域专家、产品经理、项目经理、架构师、开发经理和测试经理等。对于同样的领域知识,不同的参与者可能会有不同的理解,而在探讨过程中,通过团队达成共识,输出能够简单、清晰、准确地描述业务含义和规则的语言,即通用语言。而在后续同一个领域的软件生命周期里都使用通用语言进行交流,解决交流障碍的问题,提高协作效率

通用语言往往跟领域中的名词术语和用例场景相关,如通用语言中的名词一般可以给领域对象命名,如商品、订单等,对应领域模型中的实体对象。而动词可以表示一个动作或领域事件,如商品已下单、订单支付等,对应领域模型中的领域事件或命令。领域模型可以映射到代码,使代码更具可读性。

限界上下文(Bounded Context)

“限界”是指具体的领域边界,“上下文”是指业务语义所在的上下文环境。通过限定领域的上下文边界,项目团队就可以在这个特定的业务边界内用无歧义的通用语言进行交流。所以,限界上下文就是在限定的上下文环境内,用来封装通用语言和领域对象,保证领域内的一些术语、领域对象等有一个确切的含义,没有语义二义性的一个业务边界。

举个例子,如在电商领域,商品在销售阶段是商品,而在物流运输解读是货物,不同的业务领域下,通用语言术语不同,含义不同。而限界上下文就是用来定义这些通用语言的上下文边界的,这个边界既是业务领域的边界,也是微服务拆分和设计的边界。

充血模型与贫血模型

在充血模型中,业务逻辑都在领域实体对象中实现,实体本身不仅包含属性,还包含它的业务行为。DDD 领域模型中实体是一个具有业务行为和逻辑的对象。

而在贫血模型中,领域对象大多只有 setter 和 getter 方法,业务逻辑统一放在业务逻辑层实现,而不是在领域对象中实现。

实体(Entity)

实体是在领域模型中拥有唯一标识符,且其标志在历经各种状态或数据变更后仍能保持一致,其延续性和标志会跨越甚至超出软件的生命周期的领域对象。

在代码模型中,实体包含了实体的属性和方法,通过这些方法实现实体自身的业务行为和业务逻辑。

在领域模型映射到数据模型时,一个实体可能对应 0 个、1 个或多个数据持久化对象。大部分情况是一对一,部分场景如运行在内存中不需要持久化到数据库。

另外,不应该给实体定义太多的属性或行为,而应该寻找关联,发现其他一些实体或值对象,将属性或行为转移到其他关联的实体或值对象上。

值对象(Value Object)

值对象是一个属性的集合,由若干个基于描述目的、具有整体性概念和不可修改的属性组成,它没有一个唯一标志。

值对象物理上是独立开来的,但在逻辑上可以认为是实体属性的一部分,用于描述实体的特征。

在代码中,值对象如果是单一属性,可直接定义为实体类的属性;如果是属性集合,则可设计为值对象类。

public class Company {
	String id;  // 单一属性值对象,企业唯一ID
  String name; 
  Address address; // 属性集值对象,被 Company 实体引用
}

public class Address {
  // 地址值对象,无主键 ID
  String province;
  String city;
  String district;
  String town;
}

值对象的数据库可以采用属性嵌入放方式或序列化大对象嵌入方式(如 JSON 或 XML )进行存储,以此可以减少数据表设计,降低复杂度,提高性能。但如果要基于值对象进行快速查询和统计分析,需要根据场景考虑。

聚合(Aggregate)、 聚合根(Aggregate Root)

聚合,它通过定义对象之间清晰的所属关系和边界来实现领域模型的内聚,并避免了错综复杂的难以维护的对象关系网的形成。聚合定义了一组具有内聚关系的相关对象的集合,我们把聚合看作是一个修改数据的单元。

聚合有以下一些特点:

  1. 每个聚合有一个根和一个边界,边界定义了一个聚合内部有哪些实体或值对象,根是聚合内的某个实体;
  2. 聚合内部的对象之间可以相互引用,但是聚合外部如果要访问聚合内部的对象时,必须通过聚合根开始导航,绝对不能绕过聚合根直接访问聚合内的对象,也就是说聚合根是外部可以保持 对它的引用的唯一元素;
  3. 聚合内除根以外的其他实体的唯一标识都是本地标识,也就是只要在聚合内部保持唯一即可,因为它们总是从属于这个聚合的;
  4. 聚合根负责与外部其他对象打交道并维护自己内部的业务规则;
  5. 基于聚合的以上概念,我们可以推论出从数据库查询时的单元也是以聚合为一个单元,也就是说我们不能直接查询聚合内部的某个非根的对象;
  6. 聚合内部的对象可以保持对其他聚合根的引用;
  7. 删除一个聚合根时必须同时删除该聚合内的所有相关对象,因为他们都同属于一个聚合,是一个完整的概念;

聚合 vs 聚合根 vs 实体 vs 值对象

聚合:聚合内部业务逻辑高内聚,聚合之间满足低耦合的特点。聚合是领域模型中最小的业务逻辑边界,也是可拆分微服务的最小边界。

聚合根:聚合根是实体,有实体的特点,拥有全局唯一标志,有独立的生命周期。一个聚合只有一个聚合根,聚合根在聚合内对实体和值对象通过对象引用的方式进行组织和协调,聚合与聚合之间只能通过聚合根 ID 引用的方式实现访问和协同。

实体:实体有 ID 标志,通过 ID 判断相等性,ID 在聚合内唯一即可。实体的状态可变,它依附于聚合根,其生命周期由聚合根管理。实体一般会持久化,但与持久化对象不一定是一对一的关系。实体可以引用聚合内的聚合根、实体和值对象。

值对象:值对象无 ID,且数据不可变,它没有生命周期,用完即扔。值对象通过属性值判断相等性。它是一组概念完整的属性组成的集合,用于描述实体的状态 和特征。其核心本质是“值”。

领域事件(Domain Event)

领域事件是用于表示领域中发生的事件,如一个操作会导致进一步的操作,领域事件用来驱动业务的流转,通过数据最终一致性的策略实现领域模型的解耦,降低微服务之间直接访问的压力,实现微服务的解耦,维护领域模型的独立性。

通常采用事件表日志增量技术 + MQ 的方案,保障数据精确性,但这样存在时效性不高的问题。

仓储模式(Repository)

仓储模式是用来隔离业务实现逻辑与基础层资源实现逻辑,降低他们之间的耦合和相互影响。

仓储模式包含仓储接口和仓储实现,仓储接口面向领域层提供基础层数据处理相关的访问接口,仓储实现完成仓储接口对应的数据持久化相关逻辑处理。一个聚合会有一个仓储,统一由仓储来完成聚合数据的持久化。

当聚合内的实体需要持久化时,需要将 DO 对象转换为 PO 持久化对象,调用仓储接口。当聚合内的实体需要初始化时,调用仓库接口后,将 PO 持久化对象转换为 DO 对象。

这样基础层换数据库,或更换持久层框架,领域层都不受影响。

工厂模式(Factory)

聚合中实体和值对象等 DO 对象的创建和初始化操作必不可少,简单的可以在聚合根构造函数中进行初始化;如果依赖的值对象会较多或依赖关系复杂,可以把这部分逻辑从聚合根中剖离出来,通过工厂模式进行封装来完成初始化,让聚合根更专注于自身的领域逻辑。

通常仓储模式和工厂模式结合使用。

0

评论区