ddd 企业应用架构模式_灵魂拷问:用了DDD分包就是落地了领域驱动设计吗?谈谈DDD本质...

学习DDD的时候,作为开发,我们更关心它在技术层面的东西,尤其体现在DDD的分包方式、编码技巧等方面。

自然的,我们不禁发问,用了DDD的分包,就是实践落地了DDD了么?

不卖关子,直接说答案,并不是。

用了DDD的分包,只能说满足了DDD的"形",并没有抓住DDD的"神"。DDD的神是什么,归根到底还是面向对象,领域建模。所谓的各种分包方式本质上还是为了满足DDD面向对象的本质,方便开发者进行代码编写而提供的一种"战术设计"工具

要深入讨论这个问题,我们需要花一点时间来学习讨论一下DDD中常见的几种分包。

DDD分包概述

基于DDD的分包主要有两大流派:分层架构以及六边形架构。

分层架构以四层架构为主,基于四层架构又诞生出衍生的五层架构、六层架构等等(限于篇幅以及讨论重点,本文中我们只讨论四层架构)。

六边形架构出自 Robert C Martin(没错,就是传说中的鲍勃大叔)提出的整洁架构,后来者不断探索,又衍生出了洋葱架构。

这个过程可谓是百家争鸣。实际开发中,最为我们熟知的当属四层架构与六边形架构了,其余的各种架构都是基于这两种架构方式的变体。

四层分层架构

四层架构的分层如下图:

6ec2fe5c4f6a4c56e837e76e87299467.png

从上往下依次为:

|-userinterface    用户界面层/表示层|-application       应用层|-domain            领域层|-infrastructure    基础设施层

我们对这几层的主要功能逐个分析:

User Interface 为用户界面层(或表示层),负责为用户做信息展示以及对用户输入的命令进行解释与输出。这里指的用户可以是另一个计算机系统,不一定是使用用户界面的人。

Application 为应用层,应用层主要提供了用例级别的功能。它定义了软件要完成的任务,并且借助表达领域概念的对象来组织并解决问题,可以理解为通过胶水粘合了各种领域概念。这一层所负责的工作对业务来说意义重大,也是与其它系统的应用层进行交互的必要渠道。应用层要尽量简单,它应当尽量不包含业务规则或者知识,而只负责协调下一层中的领域对象,为领域对象分配工作, 使它们互相协作。应用层反应不了业务情况的状态,但是可以表达另外一种状态,为用户或程序显示某个任务的进度。

Domain 为领域层(或模型层),负责表达业务概念,业务状态信息以及业务规则。尽管保存业务状态的技术细节是由基础设施层实现的,但是反映业务情况的状态确实是由领域层控制并且使用的。领域层是业务软件的核心,它体现了DDD的核心:领域模型

Infrastructure 层为基础实施层,它向其他层提供通用的技术能力:为应用层传递消息,为领域层提供持久化机制,为用户界面层绘制屏幕组件,等等。基础设施层还能够通过架构框架来支持四个层次间的交互模式。

说完概念,还是不够直观表现DDD四层架构在实际开发中扮演的角色与包含的功能,稍安勿躁,我们举几个例子说明一下:

在实际开发中,User Interface层主要包含Restful消息处理/RPC 接口交互/消息消费入口,配置文件解析,等等。

Application层主要是多进程管理及调度,多线程管理及调度,多协程调度和状态机管理,跨领域业务组织与交互(比如:对外调用的出口就可以在application进行体现,也就是所谓的防腐层),等等。

Domain层主要是领域模型的实现,包括领域对象的确立,这些对象的生命周期管理及关系,领域服务的定义,领域事件的发布,等等。

infrastructure层主要是业务平台,编程框架,第三方库的封装,基础算法,等等,它为上层提供了技术层面的支持,且往往与具体的业务细节无关。

六边形架构

六边形架构也称为端口与适配器架构,一个典型的六边形架构如图

918d9fdae3954305fa1031c74cb9d088.png

六边形每条不同的边代表了不同类型的端口,端口要么处理输入,要么处理输出。对于每种外界类型,都有一个适配器与之对应,外界通过应用层API与内部进行交互。

上图中有3个客户请求均抵达相同的输入端口(适配器A、B和C),另一个客户请求使用了适配器D。假设前3个请求使用了HTTP协议(浏览器、REST和SOAP等),而后一个请求使用了AMQP协议(比如RabbitMQ)。

端口并没有明确的定义,它是一个非常灵活的概念。无论采用哪种方式对端口进行划分,当客户请求到达时,都应该有相应的适配器对输入进行转化, 然后端口将调用应用程序的某个操作或者向应用程序发送一个事件,控制权由此交给内部区域。

应用程序通过公共API接收客户请求,使用领域模型来处理请求。

我们可以将DDD战术设计建模元素Repository的实现看作是持久化适配器,该适配器用于访问先前存储的聚合实例或者保存新的聚合实例。

正如图中的适配器E、F和G所展示的,我们可以通过不同的方式实现资源库,比如关系型数据库、基于文档的存储、分布式缓存或内存存储等。

如果应用程序向外界发送领域事件消息,我们将使用适配器H进行处理。该适配器处理消息输出,而上面提到的处理AMQP消息的适配器则是处理消息输入的,因此应该使用不同的端口。

这张图是笔者从《微服务架构设计模式》中摘录出来的

f3a191d5c3743a4be6368ef8306103d4.png

它通过OrderService表达了一个订单服务,它的核心通过六边形架构组织,由业务逻辑和一个或多个与其他服务和外部应用程序连接的适配器组成。

图中,REST API Adaptor :表示入站适配器,实现REST API,这些API会调用业务逻辑(其实就是传统开发下的controller/api之类的劳什子,换了个马甲就显得高大上了)

OrderCommandHandlers:入站适配器,它接收来自消息通道的命令式消息,并调用业务逻辑(其实就是传统开发下的消息消费者,比如Kafka中的listener之类的,没什么新意)

Database Adaptor:业务逻辑调用以访问数据库的出站适配器。(好家伙,出站适配器,换了名字显得十分阳春白雪, 按照下里巴人的理解,就是 相对业务逻辑而言,数据库位于业务逻辑之外。因此持久化领域实体这操作,方向是由领域模型指向系统之外的,所以叫出站适配器)

Domain Event Publishing Adapter:将事件发布到消息代理的出站适配器(这其实就是传统开发下的消息生产者,比如Kafka这种的Producer)之所以也是出站适配器,是因为消息发送到消息中间件后,相对业务逻辑而言,也是处于业务逻辑之外。

我们可以看到,六边形架构中的出站适配器、入站适配器,一旦映射到传统开发中的概念,都是我们日常开发中经常接触到的。本质上还是换汤不换药,不得不佩服软件行业造词的能力。

阶段总结:四层架构与六边形架构的对应关系

我们对上面的讲解做一个小小的总结。

四层架构与六边形架构,表面上看起来是不同的两种架构分层方式,本质上,他们是同一事物的一体两面, 是不同的思想对于同一个事物在不同时期思考总结的产物,虽然表象不同,但本质却能够收敛对应起来的。

具体是如何对应的呢?

DDD四层架构中UserInterface和infrastructure可以对应到六边形架构中的适配器(入站和出站适配器均可,按照实际的角色具体分析对待);

四层架构中的application能够对应到六边形架构中的应用程序层;

四层架构中的domain能够对应到六边形架构中的领域模型层。

了解了DDD四层架构和六边形架构,我们又回到文章开头的问题上来.

既然说DDD在架构分层上往往能够通过四层架构、六边形架构表达,那么我们用了四层架构/六边形架构去做编码,我们不就是落地了领域驱动设计了么?

文章的开头我们直接给了回答,NO,用了分包并不代表落地了领域驱动设计。

我们经常听到一个成语,“形神兼备”。

对于DDD而言,分包只是DDD的形,如果只是一味的套用DDD的分包,很容易重新回到传统的三层架构中来,用俗话说就叫 “开倒车”,而这个过程常常伴随着代码腐化,最终会演化为《人月神话》中提到的 “焦油坑”

再谈DDD本质

这里,容我插一句与主题无关的话:

这个系列,虽然是针对DDD进行的,但是笔者却不厌其烦的试图去挖掘所谓的本质。原因在于不想将这个主题单纯的写成一个科普类的概念普及系列,如果是这样的话,倒不如直接去看书来的更快更直接

之所以不断去强调DDD的本质,还是想以我的视角,去呈现一些我在学习DDD的过程中的一些思考,提供给读者做参考,进而去努力使读者在学习过程中避免浪费时间踩坑,更深层的意图在于努力避免读者进入学习的误区。

好了,我们还是回到正题。

在之前的文章中,笔者也提到过:“DDD本质是面向对象为核心,通过领域建模还原现实世界”。

DDD作为一种指导复杂软件设计开发的方法论,其根源还是基于面向对象思想,围绕着对象本身的行为和属性,为不同对象划分边界, 并规范约束了多个对象(所谓领域)分组间的通信/交互方式。

简单的说,DDD的本质还是面向对象编程(不厌其烦,老生常谈,希望你无论如何要记住这一点),通过为对象注入有业务属性的行为,还对象以血肉;以对象建模映射现实世界的具体业务场景和真实交互行为, 通过业务概念映射代码逻辑,借助一整套的战略设计与战术设计,让系统从流程化的贫血状态机转变为具有有序业务交互的充血引擎。

我们可以说,通过DDD指导建模,到最终落地的过程,就是将真实世界的业务场景映射为领域模型及其交互流程的过程。

学习DDD的时候,作为初学者总是容易陷入它那一整套复杂方法论之中,DDD自身的概念、所谓的战略设计、所谓的战术设计, 其本质都是为了方便开发者/领域专家/业务需求方统一沟通语言,并指导模型构建最终进行代码落地的工具。

可以这么说,不论是战术设计还是战略设计,本质都是为了方便将真实世界映射到软件模型中。

有了这样的认知,再回过头去学习了解战略设计、战术设计及其衍生概念,就会更加容易。

如果只想记住一句话,那么只需要知道:DDD其实是一系列面向对象软件设计建模的方法论集合,其本质还是面向对象编程,其根本目的在于更加系统化指导复杂软件建模与落地,更好的将现实世界的复杂业务场景抽象为有序简洁易维护的软件系统。

当然,开发简洁有序易维护的软件系统,只用DDD是远远不够的,还需要有丰富的工程思想、整洁架构思想、扎实的编码功底、系统的软件工程理论等共同作用,这也是DDD这套方法论一直在发展的方向,它不断吸纳其他良好的最佳实践为己用,不断扩展边界。时至今日,DDD已经是涵盖建模、开发、测试、管理等领域的综合软件开发指导理论与思想。

正本清源,万法归宗

DDD的分包方式是领域建模的结果在代码分层上的映射,只是解决了“代码编写完成之后往哪里放”的问题,

我们甚至可以大胆的断言

即便不采用DDD的分层,还是基于原有的 nterface->service->domain->dao的分层,只要基于面向对象分析建模,

最终落地的代码他和DDD建模的结果也会殊途同归,因为

问题的根本其实还是在于领域如何划分,领域之间如何进行交互的问题

下期预告

接下来的文章中,我们将正式进入战术设计/战略设计中,不同于书籍,笔者会通过一个电商中的营销/支付/记账的业务场景,通过实战与概念穿插的方式,去进行内容的呈现。

以马克思主义为指导,通过理论与实践相结合,真正将DDD落地到实战中去。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/571427.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java常见编码

摘自:http://www.cnblogs.com/yaya-yaya/p/5768616.html红色 主要点  灰色 内容 绿色 知识点 橘色 补充内容几种常见的编码格式 为什么要编码 不知道大家有没有想过一个问题,那就是为什么要编码?我们能不能不编码?要…

准确率 召回率_机器学习中F值(F-Measure)、准确率(Precision)、召回率(Recall)

在机器学习、数据挖掘、推荐系统完成建模之后,需要对模型的效果做评价。业内目前常常采用的评价指标有准确率(Precision)、召回率(Recall)、F值(F-Measure)等,下图是不同机器学习算法的评价指标。下文讲对其中某些指标做简要介绍。本文针对二元分类器&am…

php 前置操作方法,前置操作-THINKPHP 5.0 手册最新版

前置操作可以为某个或者某些操作指定前置执行的操作方法,设置 beforeActionList属性可以指定某个方法为其他方法的前置操作,数组键名为需要调用的前置方法名,无值的话为当前控制器下所有方法的前置方法。[except > 方法名,方法名]表示这些…

Saving James Bond - Easy Version 原创 2017年11月23日 13:07:33

06-图2 Saving James Bond - Easy Version(25 分) This time let us consider the situation in the movie "Live and Let Die" in which James Bond, the worlds most famous spy, was captured by a group of drug dealers. He was sent to …

commit是直接提交到远程吗 svn_xcode 把项目代码提交到远程SVN服务器

环境 xcode 7 Mac airxcode默认支持GIT源码管理工具,但现在想把代码提交到已有到SVN服务器上,步骤如下:1,在safari中打开svn链接地址,信任证书,输入用户名密码 ,登陆成功,可以在浏览…

oracle导入 表 卡住了,oracle数据库怎么导入dmp,只导入数据不导入表结构?

使用方法:Exp parameter_namevalue or Exp parameter_name(value1,value2……)只要输入参数helpy就可以看到所有帮助.EXP常用选项1.FULL,这个用于导出整个数据库,在ROWSN一起使用时,可以导出整个数据库的结构。例如:ex…

【bzoj3991】[SDOI2015]寻宝游戏 树链的并+STL-set

题目描述 给出一棵树,初始每个点都是非必经的。多次改变某个点的必经状态,并询问从任意一个点出发,经过所有必经的点并回到该点的最小路程。 输入 第一行,两个整数N、M,其中M为宝物的变动次数。 接下来的N-1行&#xf…

vue 引入json地图_VUE中通过Echarts引入地图

渲染的方法如下 记得引入echartsimport echarts from echarts-----------------------------------init(dalian){this.$nextTick(()>{var myChart this.$echarts.init(document.getElementById(echartMap));echarts.registerMap(dalian, dalian,{});myChart.setOption({ser…

Django---admin

Django内置admin 1.配置路由 urlpatterns [url(r^admin/, admin.site.urls),] 2.定制admin 在admin.py中只需要讲Mode中的某个类注册,即可在Admin中实现增删改查的功能,如: admin.site.register(models.UserInfo) 但是,这种方式比…

vue 父组件获取接口值传到子组件_vue父组件异步获取数据传给子组件的方法

但是现在问题是父组件的数据是异步获取的,而子组件一开始就会渲染,如果此时没有传入数据,而子组件又要用到数据中的length属性时就会报错:怎么办呢?最简单的办法就是让子组件条件渲染,当有数据的时候才渲染…

python爬取数据需要注意的问题

1 爬取https的网站或是接口的时候,如果是不受信用的SSL证书,会报错,需要添加如下代码,如下代码可以保证当前代码块内所有的请求都自动屏蔽ssl证书问题: import ssl # 这个是爬取https的链接需要的,以及下…

pandas 合并所有列_Python学习:Pandas库+练习资料

pandas包含数据结构和数据处理工具的设计使得在Python中进行数据清洗和分析非常快捷。pandas经常是和NumPy,Scipy以及数据可视化工具matplotlib一起使用的。pandas支持大部分NumPy语言风格的数组计算,但最大的不同在于pandas是用于处理表格型或异质型数据的。而NumP…

oracle 取mac地址,java执行命令,得到Mac地址

public static void main(String[] args) {String os System.getProperty("os.name");System.out.println("操作系统:"os);String address "";if (os ! null && os.startsWith("Windows")) {try {String command "cmd…

solidity 十六进制字符串转十六进制bytes

pragma solidity ^0.4.16;contract Metadata {// 十六进制字符串转换成bytesfunction hexStr2bytes(string data)returns (bytes){uint _ascii_0 48;uint _ascii_A 65;uint _ascii_a 97;bytes memory a bytes(data);uint[] memory b new uint[](a.length);for (uint i 0;…

spring配置主库从库_Spring主从数据库的配置和动态数据源切换原理

原文:https://www.liaoxuefeng.com/article/00151054582348974482c20f7d8431ead5bc32b30354705000在大型应用程序中,配置主从数据库并使用读写分离是常见的设计模式。在Spring应用程序中,要实现读写分离,最好不要对现有代码进行改…

oracle rac实例切换,RAC+单实例DG的切换

RAC单实例DG的切换RAC单实例DG的搭建过程之前切换不成功,和参数设置有关。注意的参数是sid* 之类的,刚搭建好的环境racdbdg是单实例的,是备库,rac节点是主库。搭建完毕,切换了一次,刚好主库是单实例的racdb…

tablueau地图标记圆形_R语言在线地图神器:Leaflet for R包(三) 符号标记

如果还是直接写,会出现错误提示,说找不到经纬度的列像这种情况,就需要进行显式申明了:(顺便换个背景底图)leaflet(dh)%>%addProviderTiles("Esri.WorldStreetMap")%>%setView(116.4,39.9,zoom10)%>%addMarkers…

zabbix自动发现主机并加入组绑定模板

在被监控主机多的情况下,怎样将这些主机加入zabbix server进行监控呢?下面将介绍下zabbix自动发现功能 1、创建自动发现规则 创建“规则名称,配置ip范围及检查方式”,点击“增加”,完成自动发现规则的创建 2、加入组和…

oracle ora 08103,ORA-08103: 对象不再存在

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼[2017-05-17 17:13:22] Function Called in lngGetDataTableWithParametersselect distinct to_char (b.chargeactive_dat, yyyymmdd) as orderdate,to_char(a.recipeno2_int) as recipeno2_chr, b.orderexectype_int as ordertype…

kotlin 用协程做网络请求_中国电信营业厅: 感受 Kotlin 的 quot;加速度quot;

"我们手上是一个很成熟的项目,所以毫无疑问需要保留 Java 代码,目前只会在新开发的页面中使用 Kotlin,并已经感受到了它带来的便利。随着功能的迭代,我们相信更多的功能会转而使用 Kotlin。"—— 付迎鑫,电信…