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…

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 …

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

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

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

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

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

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

拖动验证码插件

拖动验证码插件 效果图: 在验证前,提交弹出验证失败,验证后弹出验证成功。 文件结构: css: #drag{ position: relative;background-color: #e8e8e8;width: 300px;height: 34px;line-height: 34px;text-align: center; …

matlab padarray函数零,matlab padarray函数

1padarray功能:填充图像或填充数组。用法:B padarray(A,padsize,padval,direction)A为输入图像,B为填充后的图像,padsize给出了给出了填充的行数和列数,通常用[r c]来表示。padval表示填充方法。它的具体值和描述如下…

[知了堂学习笔记]_网络基础知识_1.OSI参考模型(网络七层协议)

OSI参考模型是国际标准化组织ISO制定的模型,把计算机与计算机之间的通信分成七个互相连接的协议层,如图: 1.1各层功能 1、物理层 最底层是物理层,这一次负责传送比特流,它从第二层数据接收数据帧,并将帧的结…

Linux版本配置环境变量,如何linux环境下配置环境变量过程图解

jdk下载地址:在linux环境下的root同级目录下配置software目录将下载好的jdk上传到software文件夹里面(我使用的操作软件是)到software这个目录下.输入命令:cd /software输入命令:ll就可以看到我们的jdk包解压文件解压命令 tar xzvf jdk-8u111-linux-x64.tar.gz解压之…

奔腾双核linux服务器,Dell推出双核心奔腾服务器

来自业内的消息,Dell近日推出了一台采用Intel双核心 Pentium D的服务器,这将给小型服务器带来更强的运算能力。Dell PowerEdge SC430 主要面向小型企业客户,价格在499美元起。相比PowerEdge SC420,Dell为 PowerEdge SC430 装配了两…

EasyPlayerPro Windows流媒体播放器(RTSP/RTMP/HTTP/HLS/File/TCP/RTP/UDP都能播)发布啦

EasyPlayerPro简介 EasyPlayerPro是一款全功能的流媒体播放器,支持RTSP、RTMP、HTTP、HLS、UDP、RTP、File等多种流媒体协议播放、支持本地文件播放,支持本地抓拍、本地录像、播放旋转、多屏播放等多种功能特性,核心基于ffmpeg,稳…

ALSA声卡笔记2---ASoC驱动框架

1、简单了解一下ASOC 在嵌入式系统里面的声卡驱动为ASOC(ALSA System on Chip) ,它是在ALSA 驱动程序上封装的一层 分为3大部分,Machine,Platform和Codec ,三部分的关系如下图所示:其中Machine是指我们的…

python之路_自定义属性、json及其他js补充

一、自定义属性 我们知道,在前端页面渲染的过程中,我们可能会需要用到一些后端传来的参数,这种参数的获取我们一般是通过模板语言渲染得到。但是当这些参数是在js文件中被用到(js代码若是单独以文件形式被引用,模板语言…

C语言打印日历总结报告,C语言打印日历

应同学的要求,写了个日历,输入年份,输出12个月份的日历表,代码如下 :#includebool isLeap(int year){if((year%40 && year%100!0) || (year%4000))return 1;elsereturn 0;}void printCalendar(int year){int d…

java中二进制怎么说_面试常用:说清楚Java中synchronized和volatile的区别

回顾一下两个关键字:synchronized和volatile1、Java语言为了解决并发编程中存在的原子性、可见性和有序性问题,提供了一系列和并发处理相关的关键字,比如synchronized、volatile、final、concurren包等。2、synchronized通过加锁的方式&#…

qt用c语言编程 pdf下载文件,Qt 使用Poppler实现pdf阅读器的示例代码

开发环境 Qt5.5.1、Qt Creator 3.5.1Qt实现pdf阅读器和MFC实现pdf阅读器,其实原理都是差不多的。注意:这个是MinGW版本的Qt,也就是运行在GCC环境下的库,里面只包含 *.dll 和 *.a 。如果是Vistual Studio版本的Qt ,那么…

C语言工厂方法模式,工厂函数模式 (C语言实现)

工厂模式属于创建型模式,大致可以分为三类,简单工厂模式、工厂方法模式、抽象工厂模式。二. 工厂方法模式所谓工厂方法模式,是指定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子…

光绘文件 c语言 解析,AltiumDesigner输出光绘文件

1、钻孔信息生成设置打开PCB文件,放置钻孔位图符号对应的孔大小的列表:切换到DrillDrawing层放置字符串,Place->string(PS),此时按Tab键,在出现的Text文本框中点击下拉箭头,选择.Legend,OK。…

曼哈顿距离最小生成树与莫队算法(总结)

曼哈顿距离最小生成树与莫队算法(总结) 1 曼哈顿距离最小生成树 曼哈顿距离最小生成树问题可以简述如下: 给定二维平面上的N个点,在两点之间连边的代价为其曼哈顿距离,求使所有点连通的最小代价。 朴素的算法可以用O(N…