关于高大上的DDD,白话入门篇

说起DDD,感觉高大上,领域驱动设计,聚合,聚合根,小编喜欢用大白话把它说得简单些,当然术语有术语的好处,那就是提高大家沟通的准确性和统一性,欢迎留言讨论完善。

就是以业务为驱动,一个完整业务对象,比如用户,用户就包含用户基本信息,用户所在的部门,用户角色,这样才能构成一个完整的用户业务整体画像。这个业务整体就是一个聚合最后提供给service层简单调用;而这个整体业务包是由用户,权限,以及用户所属部门关联关系来组成的,而用户,权限,部门表是组成这个整体的一个不可再拆分实体(不可再拆分就叫根了,所以叫聚合根),把这几个东西组合成一个整体的业务包就叫聚合;这样做的好处是什么呢,提供给外面的都是一个整体业务包,相关数据流一致性和事务之类的都在这个业务包内完成,这个业务包丢哪里都是一个整体复用;在微服务体系下优势显得更明显,如果你的一个微服务只干一件事叫原子服务,通过对每个微服务的结合打包形成一个业务整体暴露出一个新服务就叫服务包,每个独立业务包都能独立运行

这里随便举个栗子:

公司部门权限小系统,用户表 User、角色表 Role、用户角色关系表 UserRole、部门表 Department、菜单表 Menu、菜单角色关系表 MenuRole,共六个表,

首先呢,根据我的想法,只要对一个模型操作,就必须建仓储 Repository ,那它就是一个聚合,所以:聚合和仓储基本是一对一的,有一个仓储就是一个聚合

所以现在有四个聚合了:

聚合1:Department

聚合2:Menu、MenuRole、Role 三个, 聚合根是 Menu

聚合3:User、UserRole、Department、Role 四个,聚合根是 User。

聚合4:Role、User、UserRole、MenuRole、Menu  五个,聚合根是 Role。

 

DDD中的基本概念

实体(Entity)

当一个对象由其标识(而不是属性)区分时,这种对象称为实体(Entity)。比如当两个对象的标识不同时,即使两个对象的其他属性全都相同,我们也认为他们是两个完全不同的实体。

值对象(Value Object)

当一个对象用于对事物进行描述而没有唯一标识时,那么它被称作值对象。因为在领域中并不是任何时候一个事物都需要有一个唯一的标识,也就是说我们并不关心具体是哪个事物,只关心这个事物是什么。比如下单流程中,对于配送地址来说,只要是地址信息相同,我们就认为是同一个配送地址。由于不具有唯一标示,我们也不能说这一个值对象或者那一个值对象。

领域服务(Domain Service)

一些重要的领域行为或操作,它们不太适合建模为实体对象或者值对象,它们本质上只是一些操作,并不是具体的事物,另一方面这些操作往往又会涉及到多个领域对象的操作,它们只负责来协调这些领域对象完成操作而已,那么我们可以归类它们为领域服务。它实现了全部业务逻辑并且通过各种校验手段保证业务的正确性。同时呢,它也能避免在应用层出现领域逻辑。理解起来,领域服务有点facade的味道。

聚合及聚合根(Aggregate,Aggregate Root)

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

聚合根属于实体对象,它是领域对象中一个高度内聚的核心对象。(聚合根具有全局的唯一标识,而实体只有在聚合内部有唯一的本地标识,值对象没有唯一标识,不存在这个值对象或那个值对象的说法)

若一个聚合仅有一个实体,那这个实体就是聚合根;但要有多个实体,我们就要思考聚合内哪个对象有独立存在的意义且可以和外部领域直接进行交互。

工厂(Factory)

DDD中的工厂也是一种封装思想的体现。引入工厂的原因是:有时创建一个领域对象是一件相对比较复杂的事情,而不是简单的new操作。工厂的作用是隐藏创建对象的细节。事实上大部分情况下,领域对象的创建都不会相对太复杂,故我们仅需使用简单的构造函数创建对象就可以。隐藏创建对象细节的好处是显而易见的,这样就可以不会让领域层的业务逻辑泄露到应用层,同时也减轻应用层负担,它只要简单调用领域工厂来创建出期望的对象就可以了。

仓储(Repository)

资源仓储封装了基础设施来提供查询和持久化聚合操作。这样能够让我们始终关注在模型层面,把对象的存储和访问都委托给资源库来完成。它不是数据库的封装,而是领域层与基础设施之间的桥梁。DDD 关心的是领域内的模型,而不是数据库的操作。

领域驱动设计整体架构

 那从整体架构上来说,主要分成以下四个部分,具体的在下一讲的项目整体搭建中会详细说明:

  • Presentation Layer:表现层,负责显示和接受输入;

  • Application Layer(Service):应用层,很薄的一层,只包含工作流控制逻辑,不包含业务逻辑;

  • Domain Layer(Domain):领域层,包含整个应用的所有业务逻辑;

  • Infrastructure Layer:基础层,提供整个应用的基础服务;

 

关于高大上的DDD,白话入门篇 

DDD领域驱动设计的优点就包括:

1.从技术维度实现分层:能够在每层关注自己的事情,比如领域层关注业务逻辑的事情,仓储关注持久化数据的事情,应用服务层关注用例的事情,接口层关注暴露给前端的事情。

2.业务维度:通过将大系统划分层多个上下文,可以让不同团队和不同人只关注当前上下文的开发。

3.时间维度:通过敏捷式迭代快速验证,快速修正。


DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。整个过程大概是这样的,开发团队和领域专家一起通过 通用语言(Ubiquitous Language)去理解和消化领域知识,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型,再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型。

分层架构

DDD中将系统分为UI层,应用层,领域层以及基础设施层。

关于高大上的DDD,白话入门篇

分层架构

上图中,应用层是很薄的一层,因为它只负责接收UI层传来的参数和路由到对应的领域模型,它不负责处理具体的业务逻辑。系统的业务逻辑放在了领域层中,所以,领域层在系统架构中占据了很大的面积。

在层级结构中,上层模块调用下层模块提供的服务,这里就会存在一种依赖关系,Rebort C. Martin提出的依赖倒置原则大致是如下:

上层模块不应该依赖于下层模块,两者都应该依赖于抽象;
抽象不应该依赖于实现,实现应该依赖于抽象;

这是一个面向接口编程的思想,抽象说的是抽象类或接口,实现就是具体实现了这些抽象的实现类。翻译成白话文是这样的,上下层之间应该通过接口来通讯,接口定义的位置就决定了上下层的依赖关系是否倒置。比如Application层和Domain层进行通讯,接口与接口的实现类都定义在Domain层中,这是正常的面向接口编程,不存在倒置关系。而Domain层和基础设施层进行通讯时,原本是Domain层去依赖基础设施层,如果我们将接口定义在Domain层,而实现类定义在基础设施层,那么,基础设施层就将依赖Domain层,这就是“倒置”这个词的来由。实际上,我们在做这样分层架构设计时,都是将接口定义在Domain层的。

DDD元素

在使用DDD设计系统时,主要包括Entity,Value Object,Service,Aggregate,Repository,Factory,Domain Event,Moudle等元素。

关于高大上的DDD,白话入门篇

ddd元素

在建模时,Entity可以用来代表一个事物。既然Entity是用来代表一个事物的,那么它就应该有一个唯一值来标识这个事物,同时,他还能记录这个事物状态的变化。比如:Id为5的Person对象,它的名字是张三,过两天,他将自己的名字改为李四。但是,这个人(Id=5)还是这个人(Id=5),只是他名字的状态以及发生了变化,而这个变化后的状态被Person这个Entity记录了下来。

Value Object是用来描述事物的某一方面的特征,所以它是一个无状态的,且是一个没有标识符的对象,这是和Entity的本质区别。拿订单来说,一张订单在系统中应该有一个唯一的标识,且具有状态,所以订单是一个Entity对象,而这张订单共¥100元,则是描述了这个订单的总额特征,¥100元可以用值对象表示为{100,¥},及金额和币种的组合。{100,¥}在任何限界上下文中都描述的是¥100元,是因为他们比较的是里面的内容(金额和币种),而上面的张三在修了名后,还是原来的那个人,是因为它比较的是唯一标示ID的值。

Aggregate是一组相关对象的集合,它作为数据修改的基本单元,为数据修改提供了一个边界。每个聚合都有一个根和一个边界,根也是一个实体,聚合边界以外的对象只能通过根对聚合内部元素操作。聚合将一组相关的对象内聚到一起,从而将系统的复杂程度降低。

repository用来存储聚合,相当于每一个聚合都应该有一个仓库实例。Entity和Value Object都应该具有行为,而有些行为从语义上讲,不适合放到这两个对象中,所以就单独抽象了一个Service对象,用来存放这些行为。Factory是用来生成聚合的,当生成一个聚合的步骤过于复杂时,可以将其生成过程放在工厂中。

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

要理解领域驱动,首先比较一下数据驱动与领域驱动。

不过,经常听到某些朋友提问,我要在我们公司推行DDD,但是那帮人只会数据驱动,我该咋办?

小编的意思是,如果一切看起来都还不错,为啥要推DDD?显得自己NB一点嚒?

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

关于高大上的DDD,白话入门篇

参考资料:

[1]  DDD没那么难 ,作者:秦云

肉眼品世界 

肉眼品世界,发现世界之美好(ID:find_world_fine),独立理性之观点,从技术架构,到研发管理再到商业视觉再到创业人生的升级之路,深厚的技术、研发管理体系价值观与互联网商业分析输出,助力每个个人与企业高速发展。愿景:通过共赢的方式推动社会知识创新,释放社会创新活力

关于高大上的DDD,白话入门篇

上一篇:为什么说优秀的微服务设计一定少不了 DDD?


下一篇:DDD如何设计落地?(库存,产品账示例)