灵魂拷问:用了DDD分包就是落地了领域驱动设计吗?谈谈DDD本质
学习DDD的时候,作为开发更关心它技术层面的东西,尤其体现在DDD的分包方式、编码技巧等方面。
我们不禁发问,用了DDD的分包,就是实践落地了DDD了么?
不卖关子,直接说答案,并不是。
用了DDD的分包,只能说满足了DDD的形,并没有抓住DDD的神。DDD的神是什么,归根到底还是面向对象,领域建模。所谓的各种分包方式本质上还是为了满足DDD面向对象的本质,方便开发者进行代码编写而提供的一种”战术设计”工具.
要深入讨论这个问题,我们需要花一点时间来学习讨论一下DDD中常见的几种分包。
DDD分包概述
基于DDD的分包主要有两大流派:分层架构以及六边形架构。
分层架构以四层架构为主,基于四层架构又诞生出衍生的五层架构、六层架构等等。
六边形架构出自 Robert C Martin提出的整洁架构,后续又衍生出了洋葱架构。
可谓是百家争鸣。实际开发中,最为我们熟知的当属四层架构与六边形架构了,其余的各种架构都是基于这两种架构方式的变体。
四层分层架构
四层架构的分层如下图:
从上往下依次为:
|-user interface 用户界面层/表示层
|-application 应用层
|-domain 领域层
|-infrastructure 基础设施层
我们对这几层的主要功能逐个分析:
User Interface 为用户界面层(或表示层),负责为用户做信息展示以及对用户输入的命令进行解释与输出。这里指的用户可以是另一个计算机系统,不一定是使用用户界面的人。
Application 为应用层,应用层主要提供了用例级别的功能。它定义了软件要完成的任务,并且借助表达领域概念的对象来组织并解决问题,可以理解为通过胶水粘合了各种领域概念。
这一层所负责的工作对业务来说意义重大,也是与其它系统的应用层进行交互的必要渠道。应用层要尽量简单,它应当尽量不包含业务规则或者知识,而只负责协调下一层中的领域对象,为领域对象分配工作,
使它们互相协作。应用层反应不了业务情况的状态,但是可以表达另外一种状态,为用户或程序显示某个任务的进度。
Domain 为领域层(或模型层),负责表达业务概念,业务状态信息以及业务规则。尽管保存业务状态的技术细节是由基础设施层实现的,但是反映业务情况的状态确实是由领域层控制并且使用的。领域层是业务软件的核心,它体现了DDD的核心:领域模型。
Infrastructure 层为基础实施层,它向其他层提供通用的技术能力:为应用层传递消息,为领域层提供持久化机制,为用户界面层绘制屏幕组件,等等。基础设施层还能够通过架构框架来支持四个层次间的交互模式。
说完概念,还是不够直观表现DDD四层架构在实际开发中扮演的角色与包含的功能,稍安勿躁,我们举几个例子说明一下:
在实际开发中,User Interface层主要包含Restful消息处理/RPC 接口交互/消息消费入口,配置文件解析,等等。
Application层主要是多进程管理及调度,多线程管理及调度,多协程调度和状态机管理,跨领域业务组织与交互(比如:对外调用的出口就可以在application进行体现,也就是所谓的防腐层),等等。
Domain层主要是领域模型的实现,包括领域对象的确立,这些对象的生命周期管理及关系,领域服务的定义,领域事件的发布,等等。
infrastructure层主要是业务平台,编程框架,第三方库的封装,基础算法,等等i,它为上层提供了技术层面的支持,往往与具体的业务细节无关。
六边形架构
六边形架构也称为端口与适配器架构,一个典型的六边形架构如图
六边形每条不同的边代表了不同类型的端口,端口要么处理输入,要么处理输出。对于每种外界类型,都有一个适配器与之对应,外界通过应用层API与内部进行交互。
上图中有3个客户请求均抵达相同的输入端口(适配器A、B和C),另一个客户请求使用了适配器D。
假设前3个请求使用了HTTP协议(浏览器、REST和SOAP等),而后一个请求使用了AMQP协议(比如RabbitMQ)。
端口并没有明确的定义,它是一个非常灵活的概念。无论采用哪种方式对端口进行划分,当客户请求到达时,都应该有相应的适配器对输入进行转化,
然后端口将调用应用程序的某个操作或者向应用程序发送一个事件,控制权由此交给内部区域。
应用程序通过公共API接收客户请求,使用领域模型来处理请求。
我们可以将DDD战术设计建模元素Repository的实现看作是持久化适配器,该适配器用于访问先前存储的聚合实例或者保存新的聚合实例。
正如图中的适配器E、F和G所展示的,我们可以通过不同的方式实现资源库,比如关系型数据库、基于文档的存储、分布式缓存或内存存储等。
如果应用程序向外界发送领域事件消息,我们将使用适配器H进行处理。该适配器处理消息输出,而上面提到的处理AMQP消息的适配器则是处理消息输入的,因此应该使用不同的端口。
这张图是笔者从《微服务架构设计模式》中摘录出来的
它通过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的分层,还是基于原有的 interface->service->domain->dao 的分层,只要基于面向对象分析建模,
最终落地的代码他和DDD建模的结果也会殊途同归,因为
问题的根本其实还是在于领域如何划分,领域之间如何进行交互的问题。
版权声明:
原创不易,洗文可耻。除非注明,本博文章均为原创,转载请以链接形式标明本文地址。