成为工程师 - 如何做DDD领域驱动设计?

简介: 成为工程师 - 如何做DDD领域驱动设计?

今日内容


聊完了原则,我们来看看具体的实践。今天我们就来讲如今比较推崇的DDD领域驱动设计。


由于DDD的内容多且细,本文会尝试以精简且容易理解的方式讲述DDD的重点概念及用法,目的是帮你对DDD形成一个体感上的认知。


本文篇幅略长,希望你可以耐心阅读。如果时间紧张,你可以先做收藏。


01什么是DDD


DDD的全称为domain-driven design,也就是领域驱动设计。


DDD诞生于2003年,当时并没有受到业界特别的关注,主要原因是当时的软件开发方法不注重业务模型的设计和建模,而更加偏向功能导向和技术导向。随着微服务、云计算等技术的兴起,对于复杂业务的切分和组织变得更为关键,这就需要更优秀的业务架构设计,而DDD则提供了一种有效的业务架构设计方法,逐渐受到大家的关注和认可。


虽然这些年DDD大行其道,但大家对DDD的评价却褒贬不一。一方面,DDD可以帮助构建业务规则复杂的系统,并使其保持良好的扩展性。但另一方面,DDD整体的理论比较抽象,概念偏多,这使得很多人运用的时候往往不得其法,浪费了很多时间。


所以,系统设计是否使用DDD需要一事一议。但不可否认,DDD中蕴含的设计思想是非常值得我们学习和借鉴的。


02战略设计与战术设计


为了更好地介绍DDD,我们先构建一个虚拟的业务场景。我们后面所有的内容都会围绕这个业务场景展开。


我们假设有一个小型的电商网站,卖家可以在上面出售商品,买家选择商品并购买,购买后网站把款项结算给卖家。


没错,这就是互联网最典型的应用场景。


在DDD中,可以将整体的设计分为【战略设计】【战术设计】。两者在设计的侧重点上不同,我们将分别展开说明。在整个说明过程中,重点的概念也会逐一登场。


01战略设计


战略设计的目的是:帮助开发人员和业务人员建立共同理解的业务模型,划分业务边界


这里有两个重点:【1】对齐技术人员和业务人员对于业务流程、业务规则、业务关键概念的认知,这样的好处不言而喻。【2】划分业务边界可以指导工作内容拆分。在微服务背景下,战略设计的产出可以直接指导微服务划分。


战略设计的主要产出内容如下:


我们来介绍下其中的几个概念:


【领域】是指一个具有独立性和自治性的问题空间。听着比较抽象,实际上没那么复杂。就拿我们的虚拟场景来说,领域就是“电商”。对于【领域】来说,最重要的是两个概念:范围 + 知识体系。“范围”告诉我们,领域是有边界的。“知识体系”告诉我们,领域内的概念和规则是有专业性的且相互关联的。


【子域】顾名思义,就是将领域进行划分,变成一块块更小的“领域”。每个“小领域”也必须具有“范围+知识体系”的特征。子域划分使用了分而治之的经典思想。在我们的虚拟场景中就会包括“商品域”、“支付域”、“结算域”等等。


【限界上下文】是针对子域更小的划分,其中包含了多个具体的领域模型。限界上下文的作用是将业务紧密关联的领域模型放在一起,并且提供统一的语境(各种概念在限界上下文中有统一的含义)。例如虚拟场景中,一个商品上单时我们将用户输入的卖价称之为原价,售卖时则将用户不使用任何优惠活动的价格称之为原价。


“限界上下文”对微服务拆分有重要的指导意义。


【领域模型】在“事件风暴”(我们马上会讲到)中找到的和业务相关的各种模型。例如各种单据模型、角色模型、商品模型、权限模型等等。其中,很多模型之间有非常紧密的关系。这四个概念我们是从大到小来说的,但在做战略设计的时候,其实是反过来的,这个我们后面就会看到。


02战术设计


战术设计是指:基于战略设计的结果,进一步分析领域模型并补充细节,接着将领域模型转换为具体的代码。其中主要包括【领域服务的识别】和【代码层次的划分】。我们在后面的小结马上可以看到,在这里我们先对一些关键的概念做一些了解。


【实指在业务领域中具有生命周期和业务行为并能产生变化的对象,如订单、客户等。


【值对象】是指在业务领域中用来描述某些特定属性或属性组合的对象,通常是不可变的,如地址、手机号等。


【聚合与聚合根】是用来组织“实体”和“值对象”的一种模式。聚合定义了一个业务逻辑上的整体,其中包含多个“实体”和“值对象”。聚合根是聚合中的一个实体,负责维护聚合内的完整性和一致性。


【领域服务】是指处理业务逻辑的函数,没有“实体”或“值对象”,它负责处理需要多个对象协作才能完成的复杂业务场景。


【领域事件】是指在业务领域中发生的某些重要事件,它们会对业务流程产生影响。同时,事件还将通知所有对此事件感兴趣的相关方,比如其他领域、系统或模块。


我相信,直接看这些概念会觉得生涩难懂。没关系,你只需要对这些词有点印象,下面我们结合虚拟场景来做设计时,你会再逐一看到他们。


03战略设计之领域建模


如上所述,我们首先要进行战略设计。战略设计的目的是:帮助开发人员和业务人员建立共同理解的业务模型,划分业务边界。


01事件风暴


业务模型不会直跃然纸上,我们需要通过一些方法,将大家脑中的业务规则、业务概念、业务流程以及之间的各种关系呈现出来。更重要的是,大家需要对齐这些业务相关内容的认知。【事件风暴】就是最常用的方式。


简单地说,事件风暴就是把相关的同学拉到一起,按照一套特定的组织逻辑来分析业务过程,识别业务中的关键概念。


这套特定的组织逻辑包括了“事件”、“参与方”、“命令”、“业务规则”等几个重要概念,以及他们之间的逻辑关系。这些内容指导我们开展事件风暴。

《图片来源于virtualDDD》


【参与方Actor】:参与业务流程的角色,可以是人也可以是系统。他可以出发命令,同时可以在视图上看到反馈。


【命令Command】:参与方发起的行为。命令可以触发系统行为,也可以称之为调用系统。


【外部系统ExternalSystem】:不需要知道细节的服务提供方。提供服务后会产生事件。


【领域事件DomainEvent】:业务过程中发生的重要事件。事件会激活策略。


【策略Policy】:策略负责响应领域事件,可以触发新的行为。


【视图QueryModel】:视图用来与参与方打交道,供参与方继续触发命令。


【热点Hotspot】:一些需要关注的问题,可能是业务问题(用户是否可以重复购买同一个商品),也可能是技术问题(性能瓶颈)。需要进一步讨论。


如果觉得生涩没有关系,下面我们也还会看到这些。这里你需要体会的是:分析业务有如上这些要素,他们之间相互依赖和影响。正是基于这种影响,我们才能够分析出来完整的业务流程,继而形成领域模型。


下面基于我们的虚拟场景,看看如何来做【事件风暴】。


【step1:准备工作】


工具准备。包括:一个方便大家来回走动的会议室、各种颜色的贴纸(不同颜色贴纸的作用与上面介绍的事件风暴组织图一致)、笔、一块大的黑板或者白板。


人员准备。包括:业务专家、产品、设计师、架构师等。这些人员或与系统建设相关、或了解业务规则概念、或与产品建设相关。


【step2:开场】


事件风暴过程中需要一个专职的主持人。主持人最重要的就是负责“一个接一个问问题”,驱动在事件风暴过程中产出领域模型。


开场时,主持人需要交代事件风暴会议的背景、目标、内容、要求、贴纸的用法等。


【step3:梳理事件】


之所以从事件开始,是因为事件代表某个行为的结果,是业务的重点。从事件可以正推或者反推整个业务流程,并呈现其中的各种业务概念。


例如在我们的虚拟场景中,卖家上了商品后就会有“商品已上架”事件。我们可以沿着这个事件继续思考,这个事件的前后还会有哪些事件?


“商品已上架”之前,商品肯定被创建了,所以肯定会有“商品已创建”事件。“商品已创建”后就上架了吗?一般还会有审核环节,所以会有“商品已审核”事件......


“商品已上架”之后,用户就可以进行购买。所以就会有“用户已支付”事件。“用户已支付”事件前肯定还会先创建订单,所以有“订单已创建”事件......


按照这个思路,我们就得到了如下这样的视图:


【step4:识别其他要素】


在梳理完领域事件之后,我们就可以以这些事件为线索,把其他信息给补上。


补充命令。命令就是行为,一定是有具体的行为触发了上面这些领域事件。例如“商品已创建”肯定是由“创建商品”这个命令触发的。“订单已创建”则是由于用户选择了商品后触发的等等。


补充参与方。上面的这些命令一定是由参与方触发的,可以标识出来。


补充策略。策略是对事件的响应。例如收到“商品已审核”事件,就会有策略来驱动将商品放上货架。收到“用户已支付”事件后就会驱动生成物流单。策略更广义地说就是业务规则。


其他还有补充视图、热点问题、第三方系统,我们不一一论述了,直接看下补充后的结果:

61b82f075809dd83a1043438336a8ecc.jpg

上图中:橙色是事件蓝色是命令粉色是策略绿色是界面黄色是参与方紫色是三方系统


当然,一个真正的电商网站比上述分析的内容要复杂得多。为了方便叙述,我们就挑重点的一些来介绍了。


02分析领域模型


做完了事件风暴,下面就是根据事件风暴的结果来进行建模了。我们可以从事件、命令、策略中提取名词,这些名词往往就是【领域实体】或者【值对象】。下图就是我们分析并提取的内容(我们在事件风暴基础上做了一些补充):


15767d080b05861945940aff397f25c1.jpg

接着,我们需要对这些“实体”及“值对象”做聚合。还记得上面聚合的定义吗?忘了?没关系,这里我再贴一下。


聚合定义了一个业务逻辑上的整体,其中包含多个“实体”和“值对象”。而聚合这些“实体”和“值对象”的实体称之为聚合根。


下图就是我们聚合后的结果,其中绿色的就是聚合根。



9b512d44724421c6dbb6bd82cca8f6b9.jpg

03划分限界上下文


在得到聚合和聚合根以后,我们就需要来划分限界上下文。限界上下文指导我们拆分服务,所以限界上下文圈定的内容就会放在一个系统中建设。


限界上下文划分的依据是:在一个限界上下文中有一个完整的业务子流程,并且提供了统一的语境。根据这个指导方针,我们将上面的聚合做如下划分:


f034dbeb9da38beafb912605f7911311.jpg

上图可以看到,在划定限界上下文时,我们也同时标出了各种子域。


04战术设计之代码实现


在完成限界上下文的划定后,针对每一个限界上下文就进入了战术设计阶段。这个阶段依然需要使用到我们上面分析获得的【事件风暴】结果。


我们仅以【商品限界上下文】为例来展开论述。


【step1:分析领域服务】


我们在战略设计中根据事件、命令、策略中的名词提取了领域模型中的实体和值对象。


“实体”和“值对象”偏重于表现数据,也就是这些实体包含了哪些信息。虽说实体本身也提供了业务规则相关的能力,但能力比较琐碎。我们往往需要将多个实体聚合在一起对外提供能力,而这样的能力我们就称之为“领域服务”。而将“领域服务”编排起来提供的能力就表现为了系统对外的能力。


领域服务主要来自于“事件风暴”中的命令。我们可以看到,命令有:创建商品、编辑商品、提交审核、审核通过、商品上架。

【step2:划分代码层次】


接下来我们来讲DDD经典的分层模型。

8beb4aa8ed717dbb60c07ee14f27bddb.jpg


如上图所示,在DDD分层设计中,主要分为四个层次:


【接口层】:对出入参仅做格式上的校验,不能涉及“例如用户是否在黑名单中”这样的校验。


【服务层】:负责编排流程、处理rpc请求、控制同异步。不能涉及领域概念。


【领域层】:针对领域规则来实现具体的能力,不能跨领域。


【基础层】:这一层严格意义上来说并不处于最下面一层。在上面三层中,所有的外部依赖(DB、缓存、中间件、服务调用)都使用接口的方式来避免和具体技术耦合。而基础层提供了这些接口的具体实现。


很多文章都写到:经典的DDD分层框架中,所有和外围应用打交道都应该是应用层的逻辑。所以,多个系统间的交互是如下这样:

b8872caa9ca96cbafca6ce6b086be678.jpg

不过根据实际经验来看,很多时候领域层的领域服务往往也会直接做RPC调用和异步事件交互。


事实上,DDD的分层及交互标准可以根据实际情况做一些修改,只要不破坏一些关键原则(例如依赖关系不能倒置、除了基础层不能有对外部依赖的具体实现)。切记不要为了DDD而DDD。


【step3:具体代码目录】


结合上面提到的分层,我们最后来看下具体的代码目录结构是怎样的。

bcd793477bcd08d1171f89e3b863958a.jpg

为了便于解释各个目录的作用,我就直接把说明标注在图中了。


到这里,我们就把DDD领域设计相关的内容都讲完了。

今日小结


今天我们介绍了近些年炙手可热的DDD领域驱动设计。结合我们给出的简易电商场景,给大家介绍了DDD的关键概念以及如何做从战略到战术的各种设计。

相关文章
|
设计模式 缓存 自然语言处理
DDD领域驱动设计如何进行工程化落地
DDD领域驱动设计到底如何进行实际的工程化落地,为什么要进行领域分层?本文主要围绕DDD领域分层,设计了可落地的工程结构。
DDD领域驱动设计如何进行工程化落地
|
设计模式 架构师 程序员
DDD洋葱架构才是 yyds!阿里大牛手记(DDD)领域驱动设计应对之道
虽然身为架构师,设计一个高质量的架构依然是复杂与困难的。 简单来说,动用大量的资源只为了一套优质的三高架构并不正确,而是该在了解当前业务现状的情况下,创造出灵活、可维护、健硕能成长的。
|
架构师 算法 测试技术
小团队也能做DDD-中篇
小团队也能做DDD-中篇
231 0
|
存储 开发框架 Java
「软件设计」权威领域驱动设计(DDD)简介
「软件设计」权威领域驱动设计(DDD)简介
|
Java API 领域建模
领域驱动设计(DDD)-简单落地
一、序言     领域驱动设计是一种解决业务复杂性的设计思想,不是一种标准规则的解决方法。在本文中的实战示例可能会与常见的DDD规则方法不太一样,是简单、入门级别,新手可以快速实践版的DDD。如果不熟悉DDD设计思想可看下基础思想篇 二、设计阶段     领域建模设计阶段常见的方法有 四色建模法、EventSourcing等 推荐一篇博文正确理解领域建
12147 1
|
Web App开发 机器学习/深度学习 数据可视化
OneCode 领域驱动设计(DDD)技术实践(一)
OneCode-DSM(以下简称DSM)工具集是建立是以OneCode低代码引擎为基础专注于低代码建模应用的高阶建模工具。 在OneCode引擎中,出了为普通用户提供无代码的拖动设计器,低代码的业务逻辑编排器,之外还提供了供专业业务领域专家的使用的DSM建模工具。
|
存储 设计模式 前端开发
浅谈领域驱动设计实践——董炎焱
近年来领域驱动设计(Domain Drive Design)大火。那么我们为什么要学习领域驱动设计,它适合用于哪些场景?怎么去用?在用的过程中,又有哪些需要注意的地方呢?
浅谈领域驱动设计实践——董炎焱
|
存储 消息中间件 JSON
领域驱动设计:从理论到实践,一文带你掌握DDD!
学习DDD一个半月,最开始学习DDD的原因是因为我负责的业务线,涉及的系统非常多,想借鉴领域驱动设计的思想,看后续如何对系统进行重构。在没有学习DDD之前,感觉DDD可能属于那种“虚头巴脑”的东西,学完DDD之后,感觉。。。嗯。。。真香!
1731 0
领域驱动设计:从理论到实践,一文带你掌握DDD!
|
运维 架构师 前端开发
小团队也能做DDD-上篇(1)
小团队也能做DDD-上篇(1)
261 0
小团队也能做DDD-上篇(1)