ddd领域驱动设计是什么「ddd领域建模」

互联网 2023-02-07 11:31:04

今天给大家普及一下ddd领域驱动设计是什么「ddd领域建模」相关知识,最近很多在问ddd领域驱动设计是什么「ddd领域建模」,希望能帮助到您。

一、

1.1为什么要用DDD?

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

降低复杂度,分而治之;

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

准确传达业务规则,优先考虑业务;

代码设计;

它通过边界划分简化了复杂的业务领域,帮助我们设计清晰的领域和应用边界,可以轻松实现业务和技术的统一架构演进;

分享领域知识以提高援助效率;

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

1.2 DDD效应

当谈到DDD,我们不能绕过MVC。在MVC三层架构中,我们在开发功能之前获取需求并解释它们。往往第一步是先设计表结构,逐层设计上层的dao、服务、控制器。对于产品或者用户的需求做了一层自我理解的转化。

用户的需求提出后,经过这么多层的转换,特别是R&D需求在数据库结构中转换后,业务的转换是靠主观随意的行为。一旦业务边界模糊,没有充分考虑,代码层就堆积了大量的逻辑补充,越来越难以维护。

如果我们现在要下一个电子商务订单,在向用户下订单时,它涉及到用户选择商品、下订单、支付订单和发货:

MVC架构

我们通常的做法是在分析业务需求后,设计表结构,订单表,付款表,商品表等等。然后写业务逻辑。这是第一个版本的要求。函数迭代饿了。订单支付后,我可以取消。如果我们退回订购的货物,我们需要重新添加桌子吗?那么的实现逻辑也被修改。不断地迭代函数,代码就会一层一层地堆叠起来。

DDD架构

:先划分业务边界。这其中的核心就是秩序。那么订单就是聚合逻辑在这个业务领域的体现。付款方式,商品信息,地址等。都围绕着订单实体。订单本身的属性确定后,地址只是一个属性的体现。当您构建订单的域模型时,后续的逻辑边界和仓库设计将随之而来。

DDD的总体作用概括如下:

消除信息不对称;

常规MVC三层架构中,自底向上的设计模式是颠倒的,以业务为主导,自顶向下进行业务域划分;

分而治之满足大型企业需求。

2.DDD架构

2.1 DDD等级体系结构

严格的分层架构:一层只能与直接定位的下层耦合。松散分层架构:允许上层与任何下层耦合。

在领域驱动设计(DDD)中,采用松散的分层架构,层与层之间的关系没有那么严格。每一层都可以使用其下所有层的服务,而不仅仅是下一层的服务。每一层都可能是半透明的,这意味着某些服务仅对上层可见,而其他服务对上面的所有层都可见。

分层的作用,从上到下:

用户交互层

:web请求、rpc请求、mq消息和其他外部输入被视为外部输入请求,可以修改为内部业务数据。

业务应用层

:与MVC中的服务不同,服务存储了很多业务逻辑。但是,在应用服务的实现中,它负责调度、转发、检查等。

畴层

:或者模型层,系统的核心,负责表达业务概念、业务状态信息和业务规则。也就是说,它包含了这个领域中所有复杂的业务知识抽象和规则定义。这一层主要侧重于领域对象分析,可以从实体、值对象、聚合(聚合根)、领域服务、领域事件、仓库、工厂等方面入手。

基础设施层

主要有两个方面:一是为领域模型提供持久化机制,软件需要持久化能力时需要规划;它是向其他层提供通用技术支持能力的实现,如消息通信、通用工具、配置等。

在设计开发的时候,不要把本该放在领域层的业务逻辑放到应用层,因为庞大的应用层会让领域模型失焦。久而久之,你的服务就会演变成传统的三层架构,业务逻辑就会变得混乱。

2.2各层的数据转换

每一层都有自己的特定数据,可区分如下:

VO(查看对象)

:视图对象,主要对应界面显示的数据对象。对于一个网页,或者一个SWT和SWING的界面,使用一个VO对象来对应整个界面的值。

DTO(数据传输对象)

:数据传输对象,主要用于远程调用等需要大量传输对象的地方。例如,如果我们在一个表中有100个字段,那么相应的PO就有100个属性。但是,只要在我们的界面上显示10个字段,并且客户端使用WEB服务来获取数据,就不需要将整个PO对象传输到客户端。此时,我们可以使用只有这10个属性的DTO将结果传输到客户端,这不会暴露服务器表结构。到达客户端后,如果这个对象是用来对应界面显示的,那么此时它的身份会变成VO。这里我一般指的是表示层和服务层之间的数据传输对象。

域对象

:域对象是从现实世界中抽象出来的有形或无形的业务实体。

持久对象

:持久对象,与持久层的数据结构(通常是关系数据库)形成一对一的映射关系。如果持久层是关系数据库,那么数据表中的每个字段(或几个字段)对应PO的一个(或几个)属性。最形象的理解就是,一个PO就是数据库中的一条记录。这样做的好处是可以将一个记录作为一个对象来对待,可以很容易地将它转移到其他对象上。

3.DDD的基础

在学习DDD之前,有许多基本概念需要掌握。这张图完整的总结了一下。他把DDD分成不同的等级:

最内层是值、属性、唯一标识符等。这是最基本的数据单元,但不能直接使用。

那么封装了基础数据,可以直接使用的实体,就是代码中封装的实体对象。

然后是域层,根据业务分为不同的域,比如订单域、商品域、支付域。

最后,安排业务逻辑的应用服务也可以理解为业务层。

3.1域和子域

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

该域可以进一步划分为子域。我们把划分出来的子域称为子域,每个子域对应一个更小的问题域或者更小的业务范围。

域的核心思想是逐步细分问题域,以降低业务理解和系统实现的复杂度。通过领域细分,逐步减少服务要解决的问题领域,构建合适的领域模型。

举个简单的例子,在保险领域,我们可以将保险细分为核保、收付、再保险、理赔等子领域,而核保子领域又可以继续细分为保险、保全(寿险)、矫正(财险)等子领域。

3.2核心域、一般域和支持域

根据其重要性和功能属性,子域可分为以下几类:

核心域

:决定产品和公司核心竞争力的子域。它是商业成功的主要因素,也是公司的核心竞争力。

通用域

:具有通用功能的子域,多个子域同时使用,没有太多个性化需求。

支持域

:但它既不包含决定产品和公司核心竞争力的功能,也不包含一般功能的子域。

核心域、支撑域和通用域的主要目标是通过域的划分来区分公司内部不同子域的不同功能属性和重要性,使公司对不同子域采取不同的资源投入和建设策略,其关注点也会有所不同。

很多公司的业务表面看起来很相似,但是商业模式和战略方向却大相径庭,所以公司的关注点也会不一样。在划分核心域、一般域和支持域时,结果也会大不相同。

比如同样是电子商务平台的淘宝、天猫、JD.COM、Suning.cn,商业模式就不一样。淘宝C2C网站,个人卖家对个人买家,而天猫、JD.COM、Suning.cn是B2C网站,是公司卖家对个人买家。即使Suning.cn和JD.COM都是B2C模式,Suning.cn是典型的传统线下店转型电商,而JD.COM是直销和部分平台模式。

因此,当公司建立领域模型时,我们应该结合公司的战略重点和商业模式,重点关注核心领域。

3.3通用语言和边界上下文

通用语言

:它是一种能够简单、清晰、准确地描述业务含义和规则的语言。

限界上下文

:用于封装公共语言和领域对象,提供上下文,并确保领域内的一些术语和业务相关对象(公共语言)具有确切的含义,不会产生歧义。

3.3.1通用语言

共同语言是团队的统一语言。无论你在团队中担任什么角色,都会在同领域的软件生命周期中使用统一语言进行交流。那么,共同语言的价值就很明显了。它可以解决沟通障碍的问题,使领域专家和开发人员能够相互合作,从而保证业务需求的正确表达。

这种共同语言到了现场可能还是比较模糊的。其实就是建立领域对象、属性、代码模型对象等之间的映射关系。通过代码和文字,并且这种关系可以用Excel记录下来,让R&D通过代码知道这个意思,产品或者业务方通过文字知道这个意思,这样沟通起来就不会有歧义。简单来说,其实就是统一产品和研发的说辞。

直接看下图(来自极客时代欧洲创新的实用DDD课):

3.3.2限定上下文

通用语言也有其语境环境。为了避免同一概念或语义在不同语境环境下的歧义,DDD在战略设计中提出了“有界语境”的概念,用来确定语义的域边界。

有界上下文是一个明确的语义和上下文边界,领域模型存在于边界内。通用语言中的所有术语和短语都有特定的含义。看边界的上下文,边界是域的边界,而上下文是语义环境。

通过领域的有界上下文,我们可以在统一的领域边界内用统一的语言进行交流。

3.4实体和值对象

3.4.1实体

实体=唯一标识 可变性[状态 行为]

DDD要求实体是独特的,不断变化的。意思是在一个实体的生命周期中,无论它如何变化,它仍然是同一个实体。唯一性是由唯一的身份决定的。可变性也反映了实体本身的状态和行为。

它以实体DO(域对象)的形式存在,每个实体对象都有唯一的ID。我们可以多次修改一个实体对象,修改后的数据可能与原始数据有很大的不同。

但是,因为它们具有相同的ID,所以它们仍然是同一个实体。例如,商品是商品上下文中的实体,由唯一的商品ID标识。无论这个商品的数据如何变化,商品ID不变,始终是同一个商品。

3.4.2价值对象

Value =将一个值表示为一个对象来表达一个特定的固定概念。

当你只关心一个对象的属性时,它可以作为一个值对象。我们需要把值对象当作不可变的对象,不给它任何标识,尽量避免实体对象那样的复杂。

让我们举一个订单的例子。订单是一个包含地址的实体。这个地址可以是只通过嵌入属性形成的order实体对象,也可以通过json序列化一个字符串数据,存储在DB的一个字段中。所以这个Json字符串是一个值对象。容易理解吗?

下面是一个简单的图片(同一个DDD实践课源于极客时代欧洲创新):

3.5聚合和聚合根

聚合反应

聚合:我们把一些密切相关的实体和具有相同生命周期的值对象放到一个聚合中。聚合是域对象的显式分组,旨在支持域模型的行为和不变性,同时充当一致性和事务性边界。

聚合有一个聚合根和上下文边界,它根据单一业务职责和高内聚的原则定义了哪些实体和值对象应该包含在聚合中,而聚合之间的边界是松散耦合的。这样设计的服务自然是“高内聚、低耦合”。

在DDD分层架构中,聚合属于领域层,领域层包含多个聚合,共同实现核心业务逻辑。跨多个实体的业务逻辑通过域服务实现,跨多个聚合的业务逻辑通过应用服务实现。

比如有些业务场景需要由相同的聚合A、B实体来完成,我们可以使用域服务来实现这个业务逻辑;虽然一些业务逻辑需要由聚合C和聚合D中的两个服务一起完成,但是您可以将这两个服务与应用程序服务结合起来。

聚合根

如果把聚合比作一个组织,聚合的根源就是组织的负责人。聚合的根也叫根实体,它不仅是一个实体,还是聚合的管理者。

首先,作为实体本身,具有实体的属性和业务行为,实现自己的业务逻辑。

其次,作为聚合的管理者,负责协调实体和值对象,按照固定的业务规则完成共同的业务逻辑。

最后,在聚合之间,它也是聚合的外部接口,以聚合根ID关联的方式接受外部的任务和请求,在上下文内实现聚合之间的业务协同。也就是说,聚合通过聚合根ID关联相互引用。如果需要访问其他聚合实体,必须首先访问聚合根,然后导航到聚合的内部实体。外部对象不能直接访问聚合的内部实体。

以上还是有些抽象,看下面一张图就能很好理解了(同样的DDD实战课源于极客时代欧洲创新):

简单总结一下:

通过事件的风暴(我理解为头脑风暴,但我们一般都是先通过个人了解得到实体和价值对象,再和相关核心同学交流);

这些实体和价值对象聚合成“保险聚合”和“客户聚合”,其中“保险单”和“客户”是两者的聚合根;

找出与聚合根“应用表单”和“客户”关联的所有紧密依赖的实体和值对象;

根据聚合的根、实体和值对象的依赖关系,绘制对象的引用和依赖模型。

3.6域服务和应用服务

3.6.1域服务

当一些逻辑不属于一个实体时,可以单独拿出来放到领域服务中。理想情况下,没有域服务。如果域服务使用不当,就会慢慢演变回以前所有逻辑都在服务层的情况。

何时可以使用域服务:

执行重要的业务操作。

转换域对象。

许多域对象被用作计算的输入参数,从而产生一个值对象。

应用服务

作为应用层表示层和领域层之间的桥梁,是表达用例、用户故事的主要手段。

应用层通过应用服务接口公开系统的所有功能。在应用服务的实现中,负责整理转发,将要实现的功能委托给一个或多个域对象,只负责处理业务用例的执行顺序,组装结果。这样就隐藏了领域层及其内部实现机制的复杂性。

应用层是相对较薄的层。除了定义应用程序服务之外,在这一层中,我们可以执行安全认证、权限验证、持久事务控制或向其他系统发送基于事件的消息通知,还可以用来创建发送给客户的电子邮件。

3.7领域事件

Field =事件发布 事件存储 事件分发 事件处理。

领域是领域模型中极其重要的一部分,用于表示领域中发生的事件。忽略不相关的领域活动,同时指定哪些领域专家希望跟踪或被通知,或者与其他模型对象中的状态变化相关联。

以下是对域事件的简要描述:

事件发布

:建立一个事件,需要一个唯一的ID,然后发布;

事件存储

:发布前需要存储事件,因为接收到的事件也会被存储,可用于重试或对账等。

事件分布

:在服务内部直接发布给订阅者,服务外部需要消息中间件,如Kafka、RabbitMQ

事件处理

:首先存储事件,然后处理它。

比如用户下单后,需要增加积分,赠送优惠券。如果用瀑布写代码。逻辑一个个调用,所以不同的用户给的东西不一样,逻辑就会变得又臭又长。

这里比较好的方式是用户下单成功后发布域事件,积分聚合和优惠券聚合监控订单发布的域事件进行处理。

3.8资源库[存储]

仓库介于领域模型和数据模型之间,主要用于聚集的持久化和检索。它将领域模型和数据模型分离开来,这样我们就可以专注于领域模型,而不用考虑如何持久化。

我们将暂时不使用的域对象从内存持久存储到磁盘。以后需要再次使用这个域对象时,可以根据键值在数据库中找到这个记录,然后恢复到域对象中,应用程序可以继续使用。这就是域对象持久化存储的设计思想。

是不是觉得这个内容很抽象?直接从演示中学习,你会豁然开朗很多东西。