系统讲解DDD的基础概念,结合具体的Demo讲解DDD落地场景。
往期精选(欢迎转发~~)
前言
说一下我为什么突然想学习DDD,这个肯定不是为了装X,也不是为了以后好跳槽,虽然转到人事团队也快3个月,由于之前一直做其它项目,所以现在才开始接触招聘相关的业务。因为招聘业务涉及的系统非常多,想借鉴领域驱动设计的思想,看后续如何对系统进行重构,这个就是我想学习DDD的主要原因。
既然要学习DDD,就需要去找学习资料,目前我主要的学习资料就2个,一个是极客时间欧创新老师的《DDD实战课》,还有一个就是掘金的系列博客,《DDD实战课》让我对基础概念有了非常清晰的认识,但是实战的内容感觉落地性不强;对于掘金的系列博客,里面的基础概念比较抽象,但是里面的DDD Demo,让我对DDD有了一个直观的认识,所以两者刚好可以互补。
学习DDD的时间不长,也就学习了一周多时间,后面公司还会开展DDD的系列学习课程,到时也会参加。还是按照之前的学习习惯,每学习一块内容,就通过文章将核心内容总结出来,一方面将学习的知识系统化,也相当于二次学习,另一方面也是为了便于后续好查询,同时也能给大家留一些资料,方便大家学习。
本文主要对学习内容进行总结,然后加入一些个人的理解,前面主要讲述DDD基础,后面再结合具体的Demo,讲解DDD的落地方式。
DDD简介
说到DDD,绕不开MVC,在MVC三层架构中,我们进行功能开发的之前,拿到需求,解读需求。往往最先做的一步就是先设计表结构,在逐层设计上层dao,service,controller。对于产品或者用户的需求都做了一层自我理解的转化。
用户需求在被提出之后经过这么多层的转化后,特别是研发需求在数据库结构这一层转化后,将业务以主观臆断行为进行了转化。一旦业务边界划分模糊,考虑不全,大量的逻辑补充堆积到了代码层实现,变得越来越难维护。
DDD作用:
-
消除信息不对称
-
常规MVC三层架构中自底向上的设计方式做一个反转,以业务为主导,自顶向下的进行业务领域划分
-
将大的业务需求进行拆分,分而治之
说到这里大家可能还是有点模糊DDD与常见的mvc架构的区别。这里以电商订单场景为例。假如我们现在要做一个电商订单下单的需求。涉及到用户选定商品,下订单,支付订单,对用户下单时的订单发货。
MVC架构里面,我们常见的做法是在分析好业务需求之后,就开始设计表结构了,订单表,支付表,商品表等等。然后编写业务逻辑。这是第一个版本的需求,功能迭代饿了,订单支付后我可以取消,下单的商品我们退换货,是不是又需要进行加表,紧跟着对于的实现逻辑也进行修改。功能不断迭代,代码就不断的层层往上叠。
DDD架构里面,我们先进行划分业务边界。这里面核心是订单。那么订单就是这个业务领域里面的聚合逻辑体现。支付,商品信息,地址等等都是围绕着订单实体。订单本身的属性决定之后,类似于地址只是一个属性的体现。当你将订单的领域模型构建好之后,后续的逻辑边界与仓储设计也就随之而来了。
为什么要用DDD
-
面向对象设计,数据行为绑定,告别贫血模型
-
降低复杂度,分而治之
-
优先考虑领域模型,而不是切割数据和行为
-
准确传达业务规则,业务优先
-
代码即设计
-
它通过边界划分将复杂业务领域简单化,帮我们设计出清晰的领域和应用边界,可以很容易地实现业务和技术统一的架构演进
-
领域知识共享,提升协助效率
-
增加可维护性和可读性,延长软件生命周期
-
中台化的基石
DDD术语介绍
学习DDD前,有很多基本的概念需要理解,我整理了一下,主要包括:领域、子域、核心域、通用域、支撑域、实体、值对象、聚合、聚合根、通用语言、限界上下文、事件风暴、领域事件、领域服务、应用服务、工厂、资源库。详见下面这幅图:
开始看这幅图,感觉可能是DDD的噱头,当初步学习一轮DDD后,才发现这幅图总结的真的是非常棒!其实这幅图把DDD划分了不同的层级,最里层是值、属性、唯一标识等,这个其实就是最基本的数据单位,但是不能直接使用。然后是实体,这个其实就是把基础的数据进行封装,是可以直接使用的,在代码中就是封装好的一个个实体对象。之后就是领域层,这个就是按照业务划分为不同的领域,比如订单领域、商品领域、支付领域等。最后是应用服务,这个主要是对业务逻辑进行编排,也可以理解为业务层。
领域和子域
在研究和解决业务问题时,DDD 会按照一定的规则将业务领域进行细分,当领域细分到一定的程度后,DDD 会将问题范围限定在特定的边界内,在这个边界内建立领域模型,进而用代码实现该领域模型,解决相应的业务问题。简言之,DDD 的领域就是这个边界内要解决的业务问题域。
领域可以进一步划分为子领域。我们把划分出来的多个子领域称为子域,每个子域对应一个更小的问题域或更小的业务范围。
领域的核心思想就是将问题域逐级细分,来降低业务理解和系统实现的复杂度。通过领域细分,逐步缩小服务需要解决的问题域,构建合适的领域模型。
举个例子,比如保险领域,我们可以把保险细分为承保、收付、再保以及理赔等子域,而承保子域还可以继续细分为投保、保全(寿险)、批改(财险)等子子域。
核心域、通用域和支撑域
子域可以根据重要程度和功能属性划分为如下:
-
核心域:决定产品和公司核心竞争力的子域,它是业务成功的主要因素和公司的核心竞争力。
-
通用域:没有太多个性化的诉求,同时被多个子域使用的通用功能的子域。
-
支撑域:但既不包含决定产品和公司核心竞争力的功能,也不包含通用功能的子域。
核心域、支撑域和通用域的主要目标是:通过领域划分,区分不同子域在公司内的不同功能属性和重要性,从而公司可对不同子域采取不同的资源投入和建设策略,其关注度也会不一样。
很多公司的业务,表面看上去相似,但商业模式和战略方向是存在很大差异的,因此公司的关注点会不一样,在划分核心域、通用域和支撑域时,其结果也会出现非常大的差异。
比如同样都是电商平台的淘宝、天猫、京东和苏宁易购,他们的商业模式是不同的。淘宝是 C2C 网站,个人卖家对个人买家,而天猫、京东和苏宁易购则是 B2C 网站,是公司卖家对个人买家。即便是苏宁易购与京东都是 B2C 的模式,苏宁易购是典型的传统线下卖场转型成为电商,京东则是直营加部分平台模式。因此,在公司建立领域模型时,我们就要结合公司战略重点和商业模式,重点关注核心域。
通用语言和限界上下文
-
通用语言:就是能够简单、清晰、准确描述业务涵义和规则的语言。
-
限界上下文:用来封装通用语言和领域对象,提供上下文环境,保证在领域之内的一些术语、业务相关对象等(通用语言)有一个确切的含义,没有二义性。
通用语言
通用语言是团队统一的语言,不管你在团队中承担什么角色,在同一个领域的软件生命周期里都使用统一的语言进行交流。那么,通用语言的价值也就很明了,它可以解决交流障碍这个问题,使领域专家和开发人员能够协同合作,从而确保业务需求的正确表达。
这个通用语言到场景落地,大家可能还很模糊,其实就是把领域对象、属性、代码模型对象等,通过代码和文字建立映射关系,可以通过Excel记录这个关系,这样研发可以通过代码知道这个含义,产品或者业务方可以通过文字知道这个含义,沟通起来就不会有歧义,说的简单一点,其实就是统一产品和研发的话术。
直接看下面这幅图(来源于极客时间欧创新的DDD实战课):
限界上下文
通用语言也有它的上下文环境,为了避免同样的概念或语义在不同的上下文环境中产生歧义,DDD 在战略设计上提出了“限界上下文”这个概念,用来确定语义所在的领域边界。
限界上下文是一个显式的语义和语境上的边界,领域模型便存在于边界之内。边界内,通用语言中的所有术语和词组都有特定的含义。把限界上下文拆解开看,限界就是领域的边界,而上下文则是语义环境。通过领域的限界上下文,我们就可以在统一的领域边界内用统一的语言进行交流。
实体和值对象
-
实体 = 唯一身份标识 + 可变性【状态 + 行为】
-
值对象 = 将一个值用对象的方式进行表述,来表达一个具体的固定不变的概念。
实体
DDD中要求实体是唯一的且可持续变化的。意思是说在实体的生命周期内,无论其如何变化,其仍旧是同一个实体。唯一性由唯一的身份标识来决定的。可变性也正反映了实体本身的状态和行为。
实体以 DO(领域对象)的形式存在,每个实体对象都有唯一的 ID。我们可以对一个实体对象进行多次修改,修改后的数据和原来的数据可能会大不相同。但是,由于它们拥有相同的 ID,它们依然