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

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

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

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

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

DDD分包概述

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

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

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

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

四层分层架构

四层架构的分层如下图:

从上往下依次为:

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

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

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

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

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

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

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

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

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

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

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

六边形架构

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

六边形每条不同的边代表了不同类型的端口,端口要么处理输入,要么处理输出。对于每种外界类型,都有一个适配器与之对应,外界通过应用层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的分包方式是领域建模的结果在代码分层上的映射,只是解决了“代码编写完成之后往哪里放”的问题,

我们甚至可以大胆的断言

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

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

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

下期预告

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

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

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

  1. DDD 领域驱动设计-如何 DDD?

    注:科比今天要退役了,我是 60 亿分之一,满腹怀念-??? 前几天看了园友的一篇文章<我眼中的领域驱动设计>,文中有段话直击痛点:有人误认为项目架构中加入 Repository,Doma ...

  2. ddd 访问权限_DDD 领域驱动设计-如何 DDD?

    注:科比今天要退役了,我是 60 亿分之一,满腹怀念-

  3. [理论]领域驱动设计 DDD 是啥,cqrs是啥

    父文章 如何成为一名架构师,架构师成长之路_个人渣记录仅为自己搜索用的博客-CSDN博客_架构师成长之路 [落地版]领域驱动落地 [理论版]领域驱动设计DDD 代码框架 · 语雀 子文章 如何写可维护 ...

  4. c/s三层结构信息系统的三个层次_如何使用ABP框架(2)三层架构与领域驱动设计的对比...

    本文来自长沙.NET技术社区,原创:邹溪源.全文共有8500字,读完需耗时10分钟. 题图来自@pixabay 简述 上一篇简述了ABP框架中的一些基础理论,包括ABP前后端项目的分层结构,以及后端项 ...

  5. DDD 洋葱架构才是 yyds!阿里大牛手记(DDD)领域驱动设计应对之道

    虽然身为架构师,设计一个高质量的架构依然是复杂与困难的. 简单来说,动用大量的资源只为了一套优质的三高架构并不正确,而是该在了解当前业务现状的情况下,创造出灵活.可维护.健硕能成长的. 就拿近两年程序 ...

  6. DDD洋葱架构才是 yyds,阿里架构师手记(DDD)领域驱动设计应对之道

    虽然身为架构师,设计一个高质量的架构依然是复杂与困难的. 简单来说,动用大量的资源只为了一套优质的三高架构并不正确,而是该在了解当前业务现状的情况下,创造出灵活.可维护.健硕能成长的. 就拿近两年程序 ...

  7. DDD(领域驱动设计)系列之二-应用架构

    架构这个词源于英文里的"Architecture",源头是土木工程里的"建筑"和"结构",而架构里的"架"同时又包含了& ...

  8. DDD领域驱动设计 — 贫血模型与充血模型

    文章转载来源:https://juejin.cn/post/6917125801460629518 | 前言  要想深入掌握和了解 DDD 领域驱动设计的核心,那无论如何也绕不开两大较为抽象的概念-- ...

  9. 如何使用ABP框架(2)三层架构与领域驱动设计的对比

    本文来自长沙.NET技术社区,原创:邹溪源.全文共有8500字,读完需耗时10分钟. 题图来自@pixabay 简述 上一篇简述了ABP框架中的一些基础理论,包括ABP前后端项目的分层结构,以及后端项 ...

最新文章

  1. 线上服务CPU100%问题快速定位实战--转
  2. 潭州Java中级班(day_04)
  3. java的css的块_JavaWeb--了解CSS
  4. 【二分】抄书 (jzoj 2123)
  5. java 单向链表 双向链表_java 单向链表与双向链表的实现
  6. 九龙擒庄指标源码破译_擒庄系列:庄家难逃该指标,散户屡试不爽的秘籍!(附公式)...
  7. php基本语法实验总结,PHP总结(一)基本语法内容
  8. Duplicate methods named spliterator with the parameters () and () are inherited from the types Colle
  9. 20145236《信息安全系统设计基础》第1周学习总结
  10. scrapy-redis的官方文档和源码位置
  11. Webpack 配置: 自定义网站图标 favicon
  12. 三维图像专业处理软件Dragonfly的中文语言包
  13. 【华为OD机试真题 python】 5键键盘【2022 Q4 | 100分】
  14. 新浪天气预报代码及城市代码
  15. 【React学习】React中ref的用法
  16. python+Django的web开发实例
  17. python自动生成字幕_自动生成字幕软件?
  18. Android适合手机开发的又一力证——安卓街机
  19. DTU连接自建MQTT服务器
  20. 【Tracker】KCF跟踪方法百度翻译

热门文章

  1. HTML+CSS+JS实现 ❤️CSS3图片遮罩高亮显示❤️
  2. 用matlab数学综合实验,MATLAB与数学实验(第2版)
  3. Java 判断文件是否隐藏
  4. Java自动类型转换和强制类型转换
  5. 计算机项目开发流程,产品开发项目建议流程图怎样画
  6. 八进制转换成十进制c语言程序,C语言程序 十进制、八进制、十六进制的相互转化...
  7. python twisted和flask_Python高效开发实战——Django、Tornado、Flask、Twisted(第2版)
  8. matlab 实验数据 传递函数,《传递函数MATLAB实验》.ppt
  9. linux cat cd,linux 文件系统命令 cat cd chmod
  10. 心情再差,也还是要拿起我的 JavaScript 实战重点代码