浅析ddd领域驱动设计(代码片段)

牧小农 牧小农     2022-12-31     399

关键词:

一、前言

最近公司一场有关于领域驱动设计的技术分享会,主要讲解了服务的划分,Restful API的设计,如何将抽象具有统一业务的范畴的Model,使其模块化,同时能够提炼组合多个模块,使得业务能够独立服务化,在软件开发中如何降低系统的复杂度是一个永恒的挑战,在之前都是通过一系列的设计模式或者范例来降低一些比较常见的复杂度,这些都是通过技术手段来解决技术问题,没有从根本上解决业务上的问题,但是在03年 Eric Evans 的《Domain Driven Design》 中,才是真正的从业务的角度出发,并且提供了一整套的针对纯业务开发的架构思路。为了更加深入理解,看了不少资料后,对DDD有了一点小小的个人心得,所以整理了一下,分享出来,希望能够对大家有帮助。

二、什么是DDD

DDD 是 Eric Evans 在2003年出版的书名,同时也是这个架构设计方法名的起源。Eric Evans “领域驱动设计之父”,世界杰出软件建模专家。他创建的 “Domain Language” 公司,就是致力帮助公司机构创建与业务紧密相关的软件。

DDD 不是一套架构,而是一种架构思想,所以导致在代码层面缺乏了足够的约束,因此 DDD 在实际应用中上手门槛比较高,而且在绝大部分公司中实际应用中是没有应用到的,或者说只是应用到了 DDD 部分思想 比如: 建模的思想,对整个架构体系的思想是无法落地的,而且一些依然火热的ORM工具(Hibernate)助长了贫血模型的扩散,同样因为传统的基于数据库技术以及MVC的四成架构应用(UI、Business、Data Access、Database)依然能够为我们解决绝大部分的应用开发。

之前的服务架构 局限于单机 +LB 用MVC提供的Rest接口提供外部服务调用,或者用WebService 做RPC调用,到了2014年,SOA开始火热起来了,微服务开始如雨后春笋一样的开始冒头,怎么把一个应用或者项目合理化的进行拆分多个微服务,成为了各个技术负责人的思考的重点,而在DDD里面的 Bounded Context(限界上下文)中就为我们提供了一整套合理的架构思想。

但是 DDD 可以让我们思考 在我们的项目中哪些是可以被服务化拆分,哪些业务逻辑需要被聚合在一起,实现最小的开发和维护成本。

三、领域驱动设计-基本概念

DDD 的全称为 Domain Driven Design,即领域驱动设计,DDD不是架构,而是一种方法论(Methodology),微服务架构从一出来就没有很好的理论支撑如何合理的划分服务边界。

在我们早期常见的软件开发就是拿到产品需求后,先考虑数据库设计,根据数据库设计,建立对应的实体层、服务层等等,但是这种方式会将 分析、设计和业务需求脱节,而更多的是直接考虑应该如何实现,这就有点本末倒置了,而DDD 是从问题本身出发进行的设计方法。

概念: 系统设计应该是一种以领域为核心的设计和开发,设计应该通过维护一个深度反应领域概念的模型,以及提供可行的经过实践校验的大量模式来应对领域的复杂性。

DDD 更像小颗粒的迭代设计,最小单元是 领域模型(Domain Model)

什么是领域(Domain)?

什么是领域?比如我们经常使用的某宝、某东、属于网上电商领域,那么这些领域就会有对应的商品浏览、购物车、下单、扣减库存、供应商、付款等等核心环境。再比如我们想做一个聊天系统,那这个系统的核心业务就要确定,比如有 联系人、分组、朋友圈、视频、聊天记录等功能。

所以,我们可以得知,一个领域的本质上可以理解为一个问题域,只要是同一个领域的,那么他们 一定会有相同的问题域,因此只要我们确定了系统所属的领域,那这个系统的核心业务,也就是我们要解决的关键问题,问题的范围边界也就基本确定了。

什么是设计(Design)?

DDD 中的设计主要是指领域模型的设计,为什么说是领域模型的设计而不是架构设计或者其他的设计?因为DDD是一种基于模型驱动开发的软件开发思想,强调领域模型是这个系统的核心,领域模型也是整个系统的核心价值所在,每一个领域,都有对应的领域模型,因为领域模型能够很好的帮助我们解决复杂的业务问题。
领域模型绑定了领域和代码的是吸纳,确保了最终的代码实现就一定是解决了领域中的核心问题,

四、四色原型建模

简单描述: 某个人(Party)的角色(PartyRole)在某个地点(Place)的角色(PlaceRole)用某个东西(Thing)的角色(ThingRole)做了某件事情(MomentInterval)

PartPlaceThing:简称PPT,用淡绿色表示,常见的PPT有:部门、岗位、人员、地点、物品等。

Description:简称Des,用淡蓝色表示,主要用来对PPT进行描述,常见的Des有:部门类型、岗位层级、人员类型、地点区域、物品分类等。

Role:用淡黄色表示,主要表示PPT在某个场景下扮演的角色,常见的角色有:财务类部门、管理类岗位、请假者、销售点、产品等。

MomentInterval:简称MI,用淡红色表示,主要表示在一刻或一段时间内发生的一件事情,常见的MI有:部门移动、岗位移动、员工离职、产品销售等。

MomentInteval:简称MIDetail,用淡红色表示,主要表示MI的明细。

五、分层架构

分层架构是将软件模块按照水平切分的方式分成多个层

最基本的是分层架构是三层:即表现层,领域层和数据持久层

DDD中 四层架构:表现层,应用层、领域层和基础层

四层中的应用层是对三层架构中领域层进行进一步拆分。但是无论怎么分层,业务逻辑永远在领域层。

三层架构:
- 表现层: 负责向用户展示信息和接收用户的指令。需要负责处理展示逻辑,比如用户通过我们的系
统进行信用卡还款,系统会返回三个状态未申请,处理中,处理完成。表面层需要根据这个状态给
用户返回不同的页面,根据这三个不同的状态,向用户展示不同的中文说明。
- 领域层: 负责表达业务逻辑,是整个系统的核心层。比如信用卡还款服务。
- 持久层: 提供数据查询和存储服务,包括按照状态查询信用卡。

四层架构:

  • 表现层: 同三层架构表现层。
  • 应用层: 定义软件要完成的任务,不包含业务逻辑,而是协调,比如受理用户请求的任务。负责非
    业务逻辑(批量删除、修改等)
  • 领域层: 同三层架构领域层。
  • 基础层: 为各层提供通用的技术能力。为领域层提供数据和文件存储。


分层架构最重要的是每一层关注自己的职责,持久层只负责提供查询、更新和存储数据的服务,和业务逻辑无关的,所以持久层提供按照还款状态查询信用卡的服务,这样做的好处增加复用性,后续领域层提供展示已还款的信用卡服务时能复用持久层的查询服务。

分层架构的好处

分层架构的目的是通过关注点分离来降低系统的复杂度,同时满足单一职责、高内聚、低耦合、提高可复用性和降低维护成本。
单一职责:每一层只负责一个职责,职责边界清晰,如持久层只负责数据查询和存储,领域层只负
责处理业务逻辑。
高内聚: 分层是把相同的职责放在同一个层中,所有业务逻辑内聚在领域层。这样做有什么好处呢?试想一下假如业务逻辑分散在每一层,修改功能需要去各层修改,测试业务逻辑需要测试所有层的代码,这样增加了整个软件的复杂度和测试难度。

低耦合: 依赖关系非常简单,上层只能依赖于下层,没有循环依赖。

可复用: 某项能力可以复用给多个业务流程。比如持久层提供按照还款状态查询信用卡的服务,既可以给申请信用卡做判断使用,也可以给展示未还款信用卡使用。

易维护: 面对变更容易修改。把所有对外接口都放在对外接口层,一旦外部依赖的接口被修改,只需要改这个层的代码即可。

以上这些既是分层的好处也是分层的原则,大家在分层时需要遵循以上原则,不恰当的分层会违背了分层架构的初衷。

分层架构的缺点

分层架构也有几个缺点
开发成本高: 因为多层分别承担各自的职责,增加功能需要在多个层增加代码,这样难免会增加开发成本。但是合理的能力抽象可以提高了复用性,又能降低开发成本。
性能略低: 业务流需要经过多层代码的处理,性能会有所消耗。
可扩展性低: 因为上下层之间存在耦合度,所有有些功能变化可能涉及到多层的修改。

六、领域模型(domain model)

领域模型是对领域内的概念类或现实世界中对象的可视化表示。又称概念模型、业务对象模型、领域对象模型、分析对象模型。

它专注于分析问题领域本身,发掘重要的业务领域概念,并建立业务领域概念之间的关系。

优点是系统层次结构清楚,各层之间单向依赖, Client -> (Business Facade) -> Business Logic -> Data Access Object 可见,领域对象几乎只做传输介质的用处,不会影响到层次的划分。

领域对象只是作为保存状态或者传递状态使用,它是没有生命的,只是数据没有行为的对象不是真正的对象。

七、贫血模型

贫血模型是指领域对象里只有 get 和 set 方法(一般是指POJO),所有的业务逻辑是不包含在这里面的而是放在 Business Logic层。

在使用Spring的时候,通常就暗示着我们使用的是贫血模型,我们把Domain类用来单纯地储存数据,Spring管不着这些类的注入和管理,Spring关心的逻辑层(比如单例的被池化了的Business Logic层) 可以被设计成Singleton的Bean。

假设我们这里改变一下,就在Domain类中提供业务逻辑方法,那么我们在使用Spring构造这样的数据Bean的时候会遇到很多麻烦,比如 Bean之间的引用,可能引起大范围的Bean之间的潜逃构造器的调用。

八、充血模型

大多业务逻辑和持久化放在Domain Object 里面,Business Logic只是简单封装部分业务逻辑以及控制事务、权限等,这样层次结构就变成 Client -> (Business Facade) -> Business Logic -> Domain Object -> Data Access Object。

优点:

是面向对象,Business Logic 符合单一职责,不像在贫血模型里面那样包含所有的业务逻辑太过沉重。

缺点

如何划分业务逻辑,什么样的逻辑应该放在 Domain Object中,什么样的业务逻辑应该放在Business Logic中,其实是比较模糊的。

即使划分好了业务逻辑,由于分散在Business Logic和Domain Object层中,不能更好的划分模块开发。

熟悉业务逻辑的开发人员需要渗透到Domain Logic中去,而在Domain Logic又包含了持久化,对于开发者者这是十分混乱的。

如果Business Logic要控制事务并且为上层提供一个统一的服务调用入口点,它就必须把在Domain logic里实现的业务逻辑全部重新包装一遍,完全属于重复劳动。

使用RoR开发时,每一个领域模型对象都可以具备自己的基础业务方法,通常满足充血模型的开发,充血模型更适合复杂业务逻辑的设计开发。

充血模型的层次和模块的划分是一门学问,对开发人员要求也比较高,可以考虑定义这样的一些规则:
(1) 事务控制不要放在领取模型的对象中实现,可以放在facade中完成。
(2) 领域模型对象中只保留该模型驱动的一般方法,对于业务特征明显的特异场景方法调用放在facade中完成。

九、传统的数据驱动开发模式

View、Service、Dao 这种三层模式,开发者会很自然的写出过程式代码,这种开发方式中的对象只是数据载体,而没有行为,是一种贫血对象模型,以数据为中心,以数据库ER图为设计驱动,分层架构在这种开发模式下可以认为是数据处理和实现的过程。

十、限界上下文(Bounded Context)

限界上下文:定义了每个模型的应用范围

一个业务领域可以划分成多个BC,它们之间通过Context Map进行集成。BC是一个显式的边界,领域模型Bianc便存在于这个边界之内。领域模型是关于某个特定业务员领域的软件模型,通常,领域模型通过对象模型来实现,这些对象同时包含了数据和行为,并且表达了准确的业务含义。

关于限界上下文,有一个很形象的类别,细胞和细胞膜的类比:

细胞之所以能存在,是因为细胞膜定义了什么在细胞内,什么在细胞外,而且确认了什么物质可以通过细胞膜

BC可以类比为细胞膜,大型系统由于其复杂性,对于一个对象如果采取统一建模方式,可能会产生不可预测的效果。例如书中提到的客户发票中的收费对象故障,其实类似问题也在很多地方可以看到。

十一、什么是 CQRS?

CQRS —— Command Query Responsibility Segreation(命令查询职责分离),故名思义是将 command 与 query 分离的一种模式。这种命令与查询的分离方式,可以更好地控制请求者的操作。查询操作不会造成数据的修改,因而它属于一种幂等操作,可以反复地发起,而不用担心会对系统造成影响。基于这种特性,我们还可以为其提供缓存,从而改进查询的性能。

query很好理解,就是我们经常使用到的查询。

那么 command 又是什么呢,我们可以看 CRUD,其实可以分为读(R)和写(CUD),大部分情况就是一个方法要么是执行一个Command完成一个动作,要么就是查询返回数据,比如我们回答问题的人不应该去修改问题。

只要充分理解了运用CQRS模式的意图,理解CQRS模式就变得容易了许多。下图是CQRS框架AxonFramework官方文档给出的CQRS架构图。

十二、统一语言(Ubiquitous Language)

业务人员和我们使用一样的语言,我们的程序比如让业务尽量集中在领域里,比如在传统的数据驱动里,如果说 张三喜欢李四,我们一般会这么写

UserService.Love(zhangsan, lisi)

但是我们业务人员很奇怪谁Love谁? 为什么要UserService?, 如果我们写成下面这样

zhangsan.Love(lisi)

如果我们用

Company.hire(employee)
来替代
companyservice.hire(company,employee)

这样我们就更容易让业务人员参与进来,而且代码可以更易于表示真实的业务场景。

以上是根据课堂笔记来记录的,如果有错误或者不懂的地方,欢迎大家在下面留言。

我是牧小农,怕什么真理无穷,进一步有进一步的欢喜!

ddd领域驱动设计(代码片段)

有幸参与了一些领域驱动的项目,读了一些文章,也见识了一些不伦不类的架构,感觉对领域驱动有了更进一步的认识。所以今天跟大伙探讨一下领域驱动设计,同时也对一些想要实践领域驱动设计却又无处下手,或者一些正在... 查看详情

浅析领域驱动设计

1.概要DDD(Domain-drivendesign,模型驱动设计)是一种软件设计的指导思想,而非固定的一套公式化开发模板(这样就会导致网络上出现各种基于自己或业务上的理解而产出的DDD落地的实现,会让很想学习的开... 查看详情

谈谈ddd(领域驱动设计)(代码片段)

...组织了小红花的新一期分享快速搞定数字化项目——采用领域驱动设计(DDD)建设一个电商平台,听完池总的这个分享之后,我终于是把这两年重新热起来DDD(以下称为现代DDD)和我十几年前熟悉的DDD(以下称为... 查看详情

领域驱动设计ddd的一些基础概念(代码片段)

...设计、战术设计DDD在微服务中解决的问题DDD的好处与局限领域领域、子域核心域、通用域、支撑域通用语言、限界上下文通用语言限界上下文实体、值对象实体值对象聚合和聚合根聚合聚合根如何设计聚合聚合的设计原则 &nb... 查看详情

领域驱动设计领域事件ddd分层架构(代码片段)

领域事件什么是领域事件为什么需要领域事件微服务场景下的领域事件领域事件的实现DDD的分层架构DDD分层架构如何推动微服务演进领域事件什么是领域事件在事件风暴过程中,会识别出命令、业务操作、实体等,此外还有事件... 查看详情

ddd领域驱动设计(代码片段)

x[“小萌新划重点”]第一次听别人讨论DDD场景:一个技术交流群里,讨论的好热闹...看完别人的讨论,心里在想:whatDDD...然后网上搜索下,还是一脸懵,好吧,就当是一种新的设计模式吧...前段时间参加了一次技术交流,主讲:CQR... 查看详情

ddd领域驱动设计实战(分层架构)(代码片段)

...分类1.3分层架构演进2各层职责2.1用户接口层2.2应用层2.3领域层2.4基础层3微服务架构演进微服务架构的演进案例微服 查看详情

ddd「领域驱动设计」分层架构初探(代码片段)

前言基于DDD传统分层架构实现。项目github地址:https://github.com/WuMortal/DDDSample这个分层架构是工作中项目正在使用的分层架构,使用了一段时间发现受益匪浅,所以整理好我对该分层架构的一些理解分享给大家,我对于该分层架... 查看详情

ddd(领域驱动设计)示例目录结构(代码片段)

-src-it//集成测试模块-integration//集成测试代码-resources//集成测试配置文件-ut//单元测试模块-unit//单元测试代码-main//业务代码-pkg-interfaces//用户接口层-facade//提供较粗粒度的调用接口,将用户请求委托给一个或多个应用服务进... 查看详情

谈谈ddd(领域驱动设计)(代码片段)

...组织了小红花的新一期分享快速搞定数字化项目——采用领域驱动设计(DDD)建设一个电商平台,听完池总的这个分享之后,我终于是把这两年重新热起来DDD(以下称为现代DDD)和我十几年前熟悉的DDD(以下称为... 查看详情

ddd领域驱动设计初探:聚合(代码片段)

前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的。今天看到一篇博文里面写道:越是忙人越有时间写博客。呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的东西... 查看详情

领域驱动设计实战-ddd

领域驱动设计实战领域驱动(DDD,DomainDrivenDesign)为软件设计提供了一套完整的理论指导和落地实践,通过战略设计和战术设计,将技术实现与业务逻辑分离,来应对复杂的软件系统。本系列文章准备以实战的角度来介绍DDD,首... 查看详情

领域驱动设计(ddd)实践之路(代码片段)

...接:https://mp.weixin.qq.com/s/gk-Hb84Dt7JqBRVkMqM7Eg?作者:张文博领域驱动设计(DomainDrivenDesign,DDD)其实并非新理论,大家可以看看EricEvans编著的《领域驱动设计》原稿首版是2003年,距今已十余年时间。与现在的分布式、微服务相比,... 查看详情

ddd领域驱动设计:贫血模型和充血模型详解(代码片段)

文章目录前言一、贫血模型1.介绍2.优点3.缺点4.代码样例二、充血模型1.介绍2.优点3.缺点4.代码样例三、对比分析1.为什么基于贫血模型的传统开发模式如此受欢迎?2.什么项目应该考虑使用基于充血模型的DDD开发模式?结... 查看详情

[译文]应用领域驱动设计,第一章-基础(代码片段)

应用领域驱动设计,第一章-基础AppliedDomain-DrivenDesign(DDD),Part1-Basics原文:AppliedDomain-DrivenDesign(DDD),Part1-BasicsWhenIstartedlearningdomain-drivendesigntherewasalotoftheorytotakein,EricEvansdidagreatjobexpla 查看详情

领域驱动设计(ddd)架构演进和ddd的几种典型架构介绍(图文详解)(代码片段)

👇👇关注后回复 “进群” ,拉你进程序员交流群👇👇作者丨我思知我在来源:https://blog.csdn.net/qq_32828253/article/details/110673205我们生活中都听说了DDD,也了解了DDD,那么怎么将一个新项目从头开... 查看详情

领域驱动设计(ddd)(代码片段)

领域驱动设计的概念 大家都知道软件开发不是一蹴而就的事情,我们不可能在不了解产品(或行业领域)的前提下进行软件开发,在开发前通常需要进行大量的业务知识梳理,然后才能到软件设计的层面,最后才是开发。而在... 查看详情

ddd:如何领用领域驱动设计来避免写流水账代码(代码片段)

         过去一年里我们团队做了大量的老系统重构和迁移,其中有大量的代码属于流水账代码。通常能看到是开发在对外的API接口里直接写业务逻辑代码,或者在一个服务里大量的堆接口,导致业务逻辑实际无... 查看详情