[软件建模/领域驱动设计] DDD之概述

目录

1 概述

什么是领域驱动设计(DDD)?一种软件设计思想

  • 这是一种用于复杂需求软件开发方法,将实现与核心业务概念不断发展的模型联系起来。它将重点放在问题域上,并有助于识别体系结构

  • 2004 年埃里克·埃文斯(Eric Evans)发表了《领域驱动设计》(Domain-Driven Design–Tackling Complexity in the Heart of Software)这本书,从此领域驱动设计(Domain Driven Design,简称 DDD)诞生。

DDD 核心思想是通过领域驱动设计方法定义领域模型,从而确定业务和应用边界,保证业务模型与代码模型的一致性。
DDD是一种设计思想,通过事件风暴使用通用语言对业务进行领域建模,通过限界上下文进行合理的领域拆分,可以使得领域模型转向微服务的设计和落地,从而解决复杂软件难以理解,难以演进,也可以解决微服务业务界限难以界定的问题。

  • 主要思想

利用确定的业务模型来指导业务与应用的设计和实现。
主张开发人员业务人员持续地沟通和模型的持续迭代式演化,以保证业务模型代码实现的一致性,从而实现有效管理业务复杂度,优化软件设计的目的。

  • 关键词:

功能(业务)复杂度
非功能(质量)复杂度
分而治之

DDD的优点

  • 以业务需求为导向:开发人员与业务团队沟通更好,工作效率更高

  • 整个团队使用的通用术语集: 该语言与项目的域模型相关联

  • 更好的代码: 可读性更强,重复代码更少

  • 敏捷性: 将领域专家的心智模型澄清为业务的有用模型

  • 专注于解决方案: 围绕纯模型的清晰界限使开发人员能够将他们的精力放在最重要的事情上。这使他们能够专注于解决方案


  • 面向对象设计,数据行为绑定,告别贫血模型;

  • 降低复杂度,分而治之;

  • 优先考虑领域模型,而不是切割数据和行为;

  • 业务语义显性化,准确传达业务规则,业务优先;

  • 代码即设计,通过领域设计即可很清晰的实现代码;

  • 它通过边界划分将复杂业务领域简单化,帮我们设计出清晰的领域和应用边界,可以很容易地实现业务和技术统一的架构演进;

  • 领域知识共享,提升协助效率;

  • 增加可维护性和可读性,延长软件生命周期;

  • 中台化的基石。

DDD最大的好处是:

  • 接触到需求第一步就是考虑领域模型,而不是将其切割成数据和行为,然后数据用数据库实现,行为使用服务实现,最后造成需求的首肢分离。
  • DDD让你首先考虑的是业务语言,而不是数据。
  • DDD强调业务抽象和面向对象编程,而不是过程式业务逻辑实现。
  • 重点不同导致编程世界观不同。

DDD的缺点

  • 将数据不一致的影响进一步放大,由微服务级别放大到微服务内的聚合级别。

主要概念

通用语言

  • 架起开发人员和专家之间的桥梁,融合沟通,包括对概念的统一理解和定义,以及业务人员参与到软件建模中,否则业务的变化会造成软件巨大的变化;任何出现在需求描述中的概念必须出现在领域模型中。

领域

  • 知识或活动的范围。

领域就是业务知识
业务有一些内在规则,存在专业性,比如财务、CRM、OA、电商等不同领域的业务规则不同。

  • 计算机只是业务规则的自动化

  • 在软件工程领域中,通常指的是应用程序应用的主题领域

在软件开发过程中使用的另一个常见术语是领域层领域逻辑,许多开发人员可能更熟悉该术语,即业务逻辑

  • 领域中还同时存在问题空间解决方案空间

问题空间

  • 待解决的业务问题的集合。

  • Problem Space 的拆分是基于产品/专案的商业目标,这需要团队与领域专家讨论后才能够决定。

  • 而拆分出的 Subdomain 可以依照优先等级、功能性与替代性分成核心域支持域通用域

三者的关系如下图:

解空间(解决方案空间)

  • 解空间是相对问题空间存在的,认识到解空间存在的好处是解空间可以通过一些方法从问题空间导出,而不是通过猜测得出的。

  • 解决方案空间包括一个或多个限界上下文

领域模型

  • 领域模型(Model)是业务概念在程序中的一种表达方式。

  • 领域模型可以用来设计和理解整个软件结构。

  • 面向对象设计中的概念是领域模型的一种表达方式。

与此类似,UML的建模方法也可以应用在对领域模型的表达上。
DDD 实践中,领域模型应当尽量简洁,能反应业务概念即可。
领域建模是一种描述和建模真实世界实体及其之间关系的方法,这些实体共同描述了问题领域空间,通过对系统级需求的理解,识别领域实体及其关系为理解提供了有效的基础,并帮助从业者设计可维护性、可测试性和增量开发的系统

限界上下文

  • 是特定术语和规则始终应用的域的逻辑边界。在这个边界内部,所有术语,定义和概念构成了统一语言。系统不同部分之间的明确边界,一组特定的软件模型。是一个特定的解决方案,它通过软件的方式来实现解决方案。

  • 关键词:

  • 子域提出问题空间
  • 界限上下文提供解决空间

上下文映射:是上下文关系的映射

  • 关键词:

合伙
共享内核客户-供应商
遵奉者
防腐层
开放主机
发布语言
另谋他路
大泥球

子域

  • 为了降低业务理解复杂度,DDD 实践中通常将领域划分为子域,通过分而治之的方法分析问题。

例如,在线零售商店可以将产品目录、库存和交付作为其子域。

核心域

  • 核心域是指领域中最核心的部分,通常对应企业的核心业务,核心领域对业务至关重要,至关重要,以至于为您带来竞争优势,并且是业务背后的基本概念。

  • 核心领域应该提供整个系统总价值的20%左右,大约占代码库的5%,并承担大约80%的工作。

  • 关键词:

  • 核心服务
  • 为什么要开发系统为什么不买现成的
  • 为什么不外包
  • 将关注点放在核心域上

支撑域

  • 支撑域是一种特殊的子域,是指为了实现核心业务而不得不开发的业务所对应的相关知识的集合。

比如元数据管理系统

  • 关键词:

未提供核心竞争力
支援核心
无现成方案

通用域

  • 通用域是另一种特殊的子域,对应的是业界已经有成熟方案的业务。通用域可以看做一种特殊的支撑域,可以使用标准部件来实现,短信通知、邮件等领域问题。

  • 关键词:

未提供核心竞争力,但整个系统都可能会用到它
已经解决方案,中间件服务
第三方服务

实体

  • 实体领域的概念之一,具有唯一标识,可能经常被修改,状态也会经常变化(已保存→已提交—>审核通过/审核未通过),不管状态怎么变化,实体的身份标识是不变的,依然是同一个实体。

比如一个银行账户,可以由 ID 唯一标识,币种和余额可以被修改但是还是同一个账户

  • 关键词:
  • 唯一身份标识(用户提供、系统生成UUID/GUID、持久化生成、其他限界上下文提供标识、实体间、聚合间的纽带)
  • 可变性
  • 生命周期管理(状态管理)

值对象

  • 值对象是一种特殊的领域模型不可变,通过值判断同一性。值对象没有身份标识

  • 它们是由属性而不是标识符定义的,任何属性的变化都视为新的值对象

  • 可以将值对象视为实体的一个复杂属性。

例如: 交易单中的金额由币种和数值组成,无论修改哪一个属性,金额都不再是原来的金额。

  • 关键词:
  • 无标识
  • 数据不可变
  • 可复制
  • 可代替
  • 整体性

聚合

  • 聚合由一个或多个实体值对象组成,这些实体和值对象基于一起更改的领域模型不变量

是一组生命周期强一致,修改规则强关联的实体和值对象的集合,表达统一的业务意义。

  • 聚合的意义在于:

让业务统一、一致,在面向对象中有非常重要价值。
聚合中的所有代码都应该是同步的。聚合需要在相同的上下文中,不能跨上下文。

  • 关键词:
  • 不变条件(业务规则)
  • 一致性边界
  • 多个聚合的最终一致性
  • 聚合根沟通 尽量小聚合

聚合根

  • 聚合最核心的实体,其他的实体和值对象都从属于这个实体。

  • 聚合根负责控制对其聚合的所有成员的访问

  • 要管理聚合必须使用一个聚合根,然后使用聚合根来实现发现、持久化聚合的作用,完成统一的业务意义。

  • 一个聚合中有且只有一个聚合根,聚合也可以只有一个单独的实体。

  • 关键词:

核心实体
聚合管理者

领域驱动设计的过程

对于DDD,我们需要学习什么?

DDD的知识体系

  • DDD 的核心知识体系,具体包括:领域、子域、核心域、通用域、支撑域、限界上下文、实体、值对象、聚合和聚合根等概念。

DDD的战略、战术设计

战略设计

  • 战略设计也叫战略建模,从业务视角出发,对业务需求进行拆解分析,划分子域,梳理限界上下文,通过领域语言从战略层面进行领域划分以及构建领域模型。并且在在构建领域模型的过程中梳理出业务对应的聚合、实体、以及值对象。

  • DDD 战略设计会建立领域模型,领域模型可以用于指导微服务的设计和拆分。事件风暴是建立领域模型的主要方法,它是一个从发散到收敛的过程。它通常采用用例分析、场景分析和用户旅程分析,尽可能全面不遗漏地分解业务领域,并梳理领域对象之间的关系,这是一个发散的过程。事件风暴过程会产生很多的实体、命令、事件等领域对象,我们将这些领域对象从不同的维度进行聚类,形成如聚合、限界上下文等边界,建立领域模型,这就是一个收敛的过程。

战术设计

  • 战术设计也称为战术建模,从技术视角出发,以领域模型基础,通过限界上下文作为服务划分的边界进行微服务拆分,在每个微服务中进行领域分层,实现领域服务,从而实现领域模型对于代码映射目的,最终实现DDD的落地实。包括:实体、值对象、聚合、聚合根、资源库、工厂、领域服务、领域事件、应用服务等代码逻辑的设计和实现。

DDD的战略设计

DDD的战略设计主要包括通用语言、领域/子域、限界上下文和架构风格等概念。

通用语言 - 定义上下文的含义

  • 定义:提炼领域知识的产出物,体现在两个方面:① 统一的领域术语;②领域行为描述。
  • 如何获取:统一语言就是需求分析的过程,也是团队中各个角色就系统目标、范围与具体功能达成一致的过程。
  • 强调统一:无论是与领域专家的讨论,还是最终的实现代码,都使用相同的术语。
  • 强调约束:既要有内涵也要有外延。

定义上下文的含义:在事件风暴中,通过团队交流达成共识的,能简单清晰准确地描述业务含义和规则的言语就是通用语言。

注意:通用语言贯穿 DDD 的整个设计过程。作为项目团队沟通和协商形成的统一语言,在说某通用语言时,必须要限定在某个上下文内,以确保每个上下文含义在它特定的边界内都有唯一的含义。

领域和子域 - 确定逻辑边界

落地DDD的方法

Q: 如何与业务人员聊?

  • 问清楚每个关键的业务概念事件关系

  • 问清业务流程分析业务用例提炼显示和隐含的业务规则识别业务对象

  • 问题清楚数据生命周期各阶段背后的逻辑。

谁使用数据、什么时候使用数据、如何使用数据
数据的创建/采集、存储、流转、整合、呈现与使用、分析与应用、归档和销毁

Q: DDD建模方法?

用例分析法

四色建模法

事件风暴法

Q:如何实现领域模型?

用领域模型验证能否将需求跑通

确定值对象是否作为集合的一部分还是独立存在

合理的规划集合和导航

合理规划关系和继承

Q:确定聚合

代表聚合的总概念

其他对象通常是依赖与它

识别聚合根

所有对象组合后代表一个概念

缺失一个对象则概念不完整

对象之间具有相同的生命周期

对象将具有一定的规则约束

对象间具有强关系

识别聚合

Q:运用DDD的好处

约束团队开发节奏

响应变化定位缺陷

业务逻辑和技术逻辑分离、隔离UI、业务、技术

架构有效分层

设计直接映射代码、代码体系设计

设计满足需求

DDD管理软件的复杂度

如您对DDD不了解可以先阅读 DDD主要概念以及之间的关系。

理解复杂度

  • 一个组织的生产力、创新、文化,以及根本上的成功,在很大程度上取决于它如何管理和应对数百万 依赖关系 所带来的 复杂性

  • 领域驱动设计(DDD)是一种自底向上的方法,用于管理复杂自适应系统中的复杂性。DDD的本质是揭示和建模依赖关系,使隐藏的复杂性显化

  • 理解系统相互依赖性并与之保持一致是创建可持续软件服务和高度自治团队的关键。因此,将DDD与其他系统视角(如约束理论)相结合是非常有必要的。

软件复杂度

  • 软件复杂度可以分为:技术非功能、业务复杂度

  • 技术复杂度业务复杂度并非完全独立,二者混合在一起产生的化合作用更让系统的复杂度变得不可预期,难以掌控

  • 二者变化维度、周期也不同,再加上团队规模人员流动等因素,加剧了架构腐化系统复杂性

  • 面临的典型问题:

  1. 规模上的问题:问题域庞大复杂,难以寻求解决方案
  2. 结构上的问题:开发人员将业务逻辑的复杂度与技术实现的复杂度混淆在一起
  3. 变化上的问题:随着需求的增长和变化,无法控制业务复杂度和技术复杂度

领域驱动设计的三大支柱

  1. 优化
    对齐IT与业务和客户价值的结合(对齐IT与业务与客户)

  2. 在多个层次上设计

内聚模块(即自治)

  1. 跨技能和角色合作

核心业务需求

  • DDD通过使业务价值成为一流的建模结构来解决此问题。建模技术用于将关键业务差异化的界定者从支持功能和实用程序中描述。

  • 在细粒度层次上理解业务价值是 DDD 思维的一个基本元素。

内聚模块

  • 内聚模块化是对相关概念进行分组的边界,这是管理依赖性的主要机制。

  • 代码中较差的边界意味着您的组织是建立在会放大依赖性的负面影响的基础上的。另一方面,内聚的代码边界促进了自治团队的发展。

  • 它们很少依赖于其他团队,使您的组织能够对独立的工作流进行细粒度控制。随后,您将拥有更细粒度的优先级排序能力和改进的流。

跨技能协作

DDD 对于改善跨技能协作也有独特的观点ーー业务、技术和所有感兴趣的人都应该使用统一语言

为了识别复杂性,从多个角度看待系统是至关重要的。统一语言对于将这些多个视角组合成一个单一模型,每个人都可以理解和讨论这个模型。

随着时间的推移,新的理解、变更将出现,业务优先级将发生变化,系统将不断发展。统一语言在保持每个人与不断发展的系统保持一致方面发挥着关键作用,确保隐藏的复杂性变得明确。

FAQ

Q: 聚合(Aggregate)与聚合根(Aggreagte Root)?

  • 高内聚、低耦合。有关系的实体紧密协作而关系很弱的实体被隔离
  • 把关系紧密的实体放到一个聚合中,每个聚合实体中有一个实体作为聚合根,所有对聚合内部对象的访问都通过聚合根来进行,外部对象只能持有对聚合根的引用。

这种设计既保证了数据的一致性,又降低了系统的复杂度。

  • 聚合根不仅是实体,还是所有聚合的管理者

Q: 领域事件

  • 在同一个微服务内的聚合之间的事件传递
  • 可使用进程内的通信机制完成

Q : 集成事件

  • 跨微服务传递事件,使用事件总线(EventBus)实现

Q:贫血模型 vs 充血模型

  • 贫血模型

把数据和行为分离到不同的对象之中,数据对象DTO或者ViewObject只表示数据,不包括业务逻辑,而将业务逻辑转移到 Service中。一个类中属性、成员变量没有方法

  • 充血模型

将数据和行为都合并到一起,采用标准的OO模式。一个类中属性、成员变量也有方法

X 参考文献

posted @   千千寰宇  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
历史上的今天:
2017-12-12 Linux之备份(tar)/解压与压缩(gzip,bzip2,xz)【待完善】
2017-12-12 [C++]Linux之进程间通信小结【待完善】
点击右上角即可分享
微信分享提示