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 ...
随机推荐
- 低代码Paas开发平台可以本地实施吗
低代码Paas开发平台可以本地实施吗?答案是肯定的.虽然低代码开发通常是以云端形式面向用户,也就是我们经常看到到aPaaS,而它也更加倾向于SaaS.但实际上,低代码开发平台是可以支持本地部署的,例如 ...
- Robot Framework中SSHLibrary 学习与总结
一.安装SSHLibrary 二.关键字 1.与连接相关的 Open Connection Get Connection Get Connections Switch Connection Clos ...
- Linux创建ftp并设置权限以及忘记ftp帐号(密码)修改 (转)
忘记ftp密码修改方法:1.登录服务器 cd /etc/vsftpdcat ftpusers找到对应的ftp用户名 (如果用户名也忘记了 那么 cd /etc 然后cat passwd 查看用户 ...
- Linux从头学03:如何告诉 CPU,代码段、数据段、栈段在内存中什么位置?
作 者:道哥,10+年的嵌入式开发老兵. 公众号:[IOT物联网小镇],专注于:C/C++.Linux操作系统.应用程序设计.物联网.单片机和嵌入式开发等领域. 公众号回复[书籍],获取 Linux. ...
- Java 在Word中创建邮件合并模板并合并文本和图片
Word里面的邮件合并功能是一种可以快速批量操作同类型数据的方式,常见的如数据填充.打印等.其中必不可少的步骤包括用于填充的模板文档.填充的数据源以及实现邮件合并的功能.下面,通过Java程序展示如何 ...
- python 07篇 内置函数和匿名函数
一.内置函数 # 下面这些要掌握 # len type id print input open # round min max filter map zip exec eval print(all([ ...
- C语言:进制表示
二进制由 0 和 1 两个数字组成,使用时必须以0b或0B(不区分大小写)开头 八进制由 0~7 八个数字组成,使用时必须以0开头(注意是数字 0,不是字母 o) 十六进制由数字 0~9.字母 A~F ...
- PYTHON找色不变移动
import cv2 import aircv as ac import numpy as np def wmhd(sjh): bzz0=0 bzz1=0 bzz2=0 xxa=0 yya=0 xxb ...
- HTML元素属性及意义
HTML属性可以给元素添加附加信息,设置的时候以 (属性名="属性值")成对出现. 属性值应该始终包括在引号内(单引号或双引号),html对大小写不敏感,所以属性和属性值也不区分大 ...
- SLAM十四讲第二版项目代码总结
github地址:https://github.com/gaoxiang12/slambook2/tree/master/ch13 双目视觉里程计 头文件 所有的类都在myslam命名空间中 1.co ...