DDD领域驱动理解
在理解领域驱动的时候,网上很多大谈理论的文章,这种对于初学者不是太容易接受。根据我自己的学习经历,建议按照如下几个步骤学习:
粗略的看一遍领域驱动的理论,做到心中有形,知道领域驱动是什么,解决什么问题的,大概有哪几个模块即可。
找一个具体的项目(推荐阿里的cola4),了解定义了几个module,每个module的作用是什么的,分别负责什么功能,了解每个module都依赖了其他哪些module,每个module之间是如何实现了相互调用的。
对照着项目中的module和领域驱动的理论,分析每个module分别对应理论中的哪块功能,理解该module的存在意义是什么,为什么这样设计,符合领域驱动中的哪个观点。
领域驱动设计是一个软件架构的设计理念,是一种设计思想,在代码结构上,并没有所谓的“标准”可言,只要符合领域驱动的设计理念即可。对于不同的业务系统可以设计出符合该理念、适合自己系统的框架结构。
首先理解一下常见的MVC和MVCS模型。MVC是我们说的比较多的一种模型,M-数据模型,V-数据展示,C-控制层。其中M层主要包括各种数据库实体类(entity),DAO,Mapper等和数据的存储获取有关的类。V层在web系统中主要就是各种页面,html、jsp、js、css等。而对于C的话就是常见的各种Controller,用来接收前端的各种增删改查交互,同时还负责从M层获取数据的包装和适配,请求参数的有效性校验,前端请求的各种跳转和重定向等。
但这种严格的MVC在实际开发中并不怎么用到,因为这种需要在C层做一些重复性的功能,比如AController里面需要生成一个组织架构树,用于页面展示树状组织架构,而BController里面有一个方法实获取菜单的树状结构,在这两个controller里面构造树结构的代码是重复性的,可以封装提取出来。假如把该方法放在AController里面,则在BController就要引入AController,同时调用AController的该方法,这样就会存在controller之间的依赖,并且controller也显得异常臃肿庞大。
为了解决这个问题,可以引入MVCS模型,这个模型也就是我们日常中用的最多的模型。这里的S是指的业务服务层,把一些通用的功能方法提取到S层,用来为各个Controller提供服务。在实际的开发中,一般会为各个小模块定义自己的MVCS,一个完整的模块通常会包括:UserList.jsp、UserController、UserService、UseerDao。而多个小模块组成了整个project。

上面的modules.goods、modules.system都是在同一个idea-module(idea的模块)下,以目录来做物理隔离的。有时候会根据情况把modules目录下的功能模块独立出来,当做一个project下面的独立idea-module来处理。

那么什么是领域驱动呢?在我的理解中就是把MVCS中的S层更加细粒度化,在“领域”的维度上进行拆分,实现每个领域的“高内聚低耦合”,以便达到领域之间的物理和逻辑的隔离。每个领域的业务高度内聚在一起,通过充血模型,实现自己的领域业务,同时暴露出接口供外部调用。
在传统MVCS中的S层,通常会根据某个对象创建一个service,例如UserService、CompanyService。或者根据功能创建一个service,例如LoginService、MessageService。这些只是简单的把一些可公用的方法简单的在物理层面放在一个java文件中,实现了简单的物理隔离。这种维度的拆分过于微观,是在方法粒度上进行的隔离,虽然实现了方法的共享,但是在整个service中取糅合着各种各样的功能。例如:定义一个OrderService,里面有订单的添加、修改、删除逻辑,还有订单的结算逻辑。但从领域角度考虑,订单的增删改查属于订单管理的业务(领域),而订单的结算属于支付相关的业务(领域),两个不同的业务逻辑代码却在相同的service中,这样安排会显得逻辑混乱。而按照领域驱动的思想,把同一领域的相关操作整合在同一domain下面,且只负责与该领域相关的操作,同时暴露出功能接口供外部调用。领域之间可以直接相互依赖聚合。当领域内部操作需要依赖其他服务(例如修改db数据)时,可以通过自定义接口方式满足领域内部操作,然后由其他领域外部服务实现该接口。
下面就用一个具体的领域驱动框架cola4来详细剖析下领域驱动的设计理念。
项目示例在:https://github.com/alibaba/COLA/tree/master/samples ,可以下载后直接导入idea。
导入后的结构如下:

分别对应cola中的五个模块:

这里的五个idea-module分别是client、adapter、application(app)、domain、infrastructure,五个module的依赖关系如图所示:
client不依赖项目中的其他模块(图中的cola组件不属于项目中的模块)
application依赖domain和client模块,同时会通过domain间接依赖infrastructure
adapter直接依赖于application,同时通过application间接依赖client
domain不依赖任何其他模块,其内部会在gateway中定义领域网关接口
infrastructure依赖于domain,在gatewayImpl中实现领域接口
其五个模块的详细功能如下:
adapter
适配层,一般是充当controller,对不同的请求方(页面/RCP)提供服务。对于未前后端分离的web系统,可以把静态页面放在该module下面。该层主要依赖于client层。
adapter依赖于client,在controller接收到请求之后,需要调用定义在client中的interface执行后续流程。
controller中接收的入参和出参也是定义在client中
client(facade)
有些文章会把client叫做facade,其目的是用来暴露接口和定义传递数据的。在client里面会定义一些interface和DTO、BO、VO等,当框架支持CQRS的时候,也会把各种event放在该层中。
controller调用的接口都会定义在该层中,同时各种入参和出参也在该层定义。
client层不依赖其他任何模块
application(facadeImpl)
application层实现了各种功能,供其他模块调用,主要是adapter中的各种controller。
application实现了定义在client中的各种接口,而client接口中定义的方法就是暴露出供adapter层调用的功能。
application中会定义各种executor来实现各种功能的具体逻辑
executor在执行业务逻辑的时候,通常会调用domain进行业务处理,其依赖于domain模块。如果是简单的逻辑也会直接调用infrastructure层,例如一些简单数据的存储等
domains
领域层主要包括领域对象(domain)、领域服务(service)、和领域网关(gateway)。
领域对象entity不同于DO和DTO。DO(data object)是属于数据层的对象和db表做一一对应;DTO(data transmission object)是在adapter层定义的数据传输对象,充当传输媒介。而领域对象定义在domain中,按照充血模型定义,在其内部实现各个领域的业务操作。
domain不依赖其他任何层,当需要调用其他模块服务时,则根据依赖倒置原则,在gateway里面定义个接口,domain的业务层调用该接口的方法完成整个业务逻辑,而接口的实现则放在Infrastructure实现。gateway中的领域接口也可以直接供application层调用。
infrastructure
infrastructure为基础服务层,主要处理和外部系统的交互。例如数据库操作、redis操作、RPC调用等
gatewayImpl实现domain层的领域网关
支持各种config配置
infrastructure中需要对领域对象转换为DO进行操作
下面就看下example中的add请求是怎么处理的。
首先在adapter中定义了一个MetricsController,该controller对外提供了addATAMetric方法,同时里面依赖了client中的com.alibaba.craftsman.api.MetricsServiceI接口。同时注意入参ATAMetricAddCmd,同样定义在client中。

之后找到addATAMetric的实现类是在app中,如下图所示。同时app中又依赖于其内部定义的各个executor完成对应的操作。

executor以组件形式定义在app中,同时调用domain中的领域接口,执行后续的业务操作。

定义在domain中的领域网关:

而领域网关的实现放在了infrastructure中:

infrastructure接收到定义在domain中的MetricItem领域对象后,首先转换为MetricDO对象,然后调用metricMapper插入数据库中。由于这里使用了CQRS,对数据的写操作需要通知到读模块,因此这里发布了一个MetricItemCreatedEvent,通知读模块更新数据。

通过上面的方式,跟着controller提供的接口,顺藤摸瓜一步一步跟踪到infrastructure层,就会对整个cola项目结构有个清晰的理解,然后再结合着领域驱动的设计思想,大概就明白了各个层级和接口的设计目的。
参考文章:
https://www.bianchengquan.com/article/539687.html
https://blog.csdn.net/significantfrank/article/details/100074716
https://www.cnblogs.com/duanxz/p/9922170.html
DDD领域驱动理解的更多相关文章
- 浅谈我对DDD领域驱动设计的理解
从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...
- (转载)浅谈我对DDD领域驱动设计的理解
原文地址:http://www.cnblogs.com/netfocus/p/5548025.html 从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来 ...
- DDD领域驱动设计的理解
DDD领域驱动设计的理解 从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能 ...
- 浅析DDD——领域驱动设计的理解
浅析DDD--领域驱动设计的理解 我觉得领域驱动设计概念的提出,是为了更清晰的区分边界.这里的边界包括业务边界和功能的边界,每个边界都包含具体的领域对象,当业务和功能的领域对象一一对应上之后,业务的变 ...
- DDD 领域驱动设计-商品建模之路
最近在做电商业务中,有关商品业务改版的一些东西,后端的架构设计采用现在很流行的微服务,有关微服务的简单概念: 微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成.系统中的各个微服务可被独 ...
- DDD 领域驱动设计-谈谈 Repository、IUnitOfWork 和 IDbContext 的实践(1)
好久没写 DDD 领域驱动设计相关的文章了,嘎嘎!!! 这几天在开发一个新的项目,虽然不是基于领域驱动设计的,但我想把 DDD 架构设计的一些东西运用在上面,但发现了很多问题,这些在之前的短消息项目中 ...
- DDD 领域驱动设计-“臆想”中的实体和值对象
其他博文: DDD 领域驱动设计-三个问题思考实体和值对象 DDD 领域驱动设计-三个问题思考实体和值对象(续) 以下内容属于博主"臆想",如有不当,请别当真. 扯淡开始: 诺兰的 ...
- DDD 领域驱动设计-三个问题思考实体和值对象(续)
上一篇:DDD 领域驱动设计-三个问题思考实体和值对象 说实话,整理现在这一篇博文的想法,在上一篇发布出来的时候就有了,但到现在才动起笔来,而且写之前又反复读了上一篇博文的内容及评论,然后去收集资料, ...
- DDD 领域驱动设计-三个问题思考实体和值对象
消息场景:用户 A 发送一个消息给用户 B,用户 B 回复一个消息给用户 A... 现有设计:消息设计为实体并为聚合根,发件人.收件人设计为值对象. 三个问题: 实体最重要的特性是什么? Messag ...
随机推荐
- 使用RSA和DES保护的Socket通信
基本要求:将DES加密算法应用于网络通信,使用RSA算法自动分配密钥,设计好界面,可验证自动生成的密钥和加解密正确的结果. 具体实现要求:客户端和服务器建立连接后,客户端生成一个随机DES密钥;服务器 ...
- AcWing 100. 增减序列
给定一个长度为n的数列每次可以选择一个区间 [l,r],使下标在这个区间内的数都加一或者都减一. 求至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列可能有多少 ...
- C++11运算符重载详解与向量类重载实例(<<,>>,+,-,*等)
1. C++运算符重载介绍 C ++ 中预定义的运算符的操作对象只能是基本数据类型.但实际上,对于许多用户自定义类型(例如类),也需要类似的运算操作.这时就必须在C ++ 中重新定义这些运算符,赋予已 ...
- 小刻也能看懂的Unraid系统使用手册:基础篇
小刻也能看懂的Unraid系统使用手册 基础篇 Unraid系统简介 Unraid 的本体其实是 Linux,它主要安装在 NAS 和 All in One 服务器上,经常可以在 Linus 的视频里 ...
- ssh服务两句话
ssh服务采用"非对称密钥系统":主要通过两把不一样的公钥和密钥来进行加密与解密的过程 公钥(Public Key):提供给远程主机进行数据加密 私钥(Private Key):远 ...
- C语言:整数保存 原码 反码 补码
#include <stdio.h> /* 本题结果为:-4 short类型占据2字节 ;赋值后实际占据了3个字节,所以有溢出警告提示,结果只保留0xfffc 保存二进制:1111 111 ...
- Java基础00-面向对象基础13
1. 类和对象 1.1 什么是对象 1.2 什么是面向对象 1.3 什么是类 1.4 什么是对象的属性 1.5 什么是对象的行为 行为就是对象能够干什么 1.6 类和对象 ...
- jquery性能优化建议-上篇
一.注意定义jQuery变量的时候添加var关键字这个不仅仅是jQuery,所有javascript开发过程中,都需要注意,请一定不要定义成如下:$loading = $('#loading'); / ...
- python -- 程序异常与调试(程序调试)
一.程序调试 A.使用assert语句检测程序代码中的错误. assert 表达式[, 参数] 如果表达式为True,则继续往下运行:如果为False,则抛出一个AssertionError异常,并且 ...
- 微信小程序云开发-云存储的应用-云相册
一.准备工作 1.创建数据库表images 2.设置数据库表images的权限 二.创建图片列表页 创建图片列表页imageList,用于展示图片列表.该页面具有跳转到图片上传页面.图片列表展示.删除 ...