为什么需要领域驱动

在一个项目的生命周期中,随着项目需求的变动,我们需要不断地改变、扩展项目中软件的功能。在之前的文章《如何阻止软件退化》我们了解到,如果在不改变原有功能框架的前提下,对功能进行直接的添加,那么软件会虽然功能的增加很快就被腐化。这是因为,我们在最开始进行框架设计的时候,是根据最初提出的客观世界规律来设计的,而并没有根据新的、更细节的客观规律进行设计。而为了避免这种腐化,我们需要在进行新的功能开发前,先对原有功能根据新的需求进行结构上的调整,然后在这个基础上在进行新功能的编写,也就是我们的前文的“两步走”。

但是这个结论看似正确,但其实只是一个理想化的结论。因为这个结论是有假设前提的,它的前提是:

我们了解,并清楚前后需求中的客观规律的关联性。

为了说明这个假设,我们以前文中的支付作为例子。我们再回顾一下前后几次的变更:

  1. 针对支付价格增加了钱款计算的分支逻辑
  2. 针对人员信息增加了人员身份获取逻辑
  3. 针对支付价格增加了支付路由判断逻辑

我们能从新增的能力中清晰地判断出前后的关联关系,但并不是所有的改动都有这样清晰的关联关系。当进行了一次变更、两次三次变更的时候关系可能还比较明确,但如果经历了30次、40次变更之后。究竟这个调整内容还是之前的关联逻辑吗(忒修斯之项目)。如果逻辑的边界变得模糊,我们又如何保证对于框架的调整是符合客观逻辑的呢?

那么,领域驱动就是为了解决这个问题而提出的。

软件是为了还原客观世界的逻辑规律,而产品需求则是提炼后的部分客观世界的规律。尽管客观世界逻辑太过于复杂无法直接实现,但是我们却可以直接参考客观世界的规律,以验证项目中逻辑的正确性,从而判断是否和产品需求要求一致。而为了可以和客观世界进行参考,就需要项目中的模型与真实世界一一对应,那么:

我们称这种项目与客观世界中一一对应的模型就是领域。

如果直接参考真实世界的客观规律,所以每一次调整完毕框架逻辑后,可以直接根据客观世界进行验证,而不用比较项目调整前后的关联性。所以:

我们需要领域驱动,因为他帮我们解决了大量迭代后项目逻辑框架与客观世界逻辑失真(由于累积的调整误差)的问题。

领域的映射

我们需要将客观世界中的逻辑映射到软件项目中,那么他们必然需要一种映射的逻辑:

  1. 客观世界的物体 -> 软件项目的对象
  2. 客观世界的行为 -> 软件项目的方法
  3. 客观世界的关系 -> 软件项目的关联

而这些客观规律的映射,就组成了我们所说的领域模型。(我们可以从中看出,领域模型是面向对象的又一次升级。)

当我们有了一个领域模型之后,因为领域模型所描述的是客观世界规律到软件中,所以我们可以根据领域模型的变更来指导程序设计。

所以,当我们有一个新的需求变更的时候,第一步就变成了先将需求通过领域模型分析。如果领域模型原本原本的客观规律不具备这种能力,则先维护领域模型(例如新增属性、新增行为)。在完成了领域模型的维护后,就可以根据维护后的领域模型来指导软件程序的变更。从而让我们整体软件的设计质量得到提高。

有人可能会说,自己在进行开发的时候也是根据这样的思路进行开发的呀,那我平时就是在用领域模型设计吗?其实,领域模型设计这种解除耦合的思想会与我们在进行开发时的抽象设计不谋而合。但主要的区别在于,领域模型设计因为出发点是客观世界,所以保证了客观直接和需求的一致性,这个基础上指导程序开发,就保证了软件和需求的一致性。而开发人员直接根据需求进行的软件抽象,无法保证这种一致性,所以当多次功能调整后,就会出现软件与客观规律产生失真,软件产生了很多自己的“规则”,而直接的结果就是用户觉得软件“不好用”。

支付的领域模型设计

还是之前的例子,没办法,就这个例子熟悉。

拿出来最开始的伪代码:

查询人员计算付款信息返回支付用路由信息

这个支付流程整体看起来就不是很难,那么那的需求描述可能是这个样子的:

  • 用户进行下单,调用支付功能。
  • 查询用户的信息比如名称、地址。
  • 计算商品待支付金额
  • 生成订单
  • 调用第三方支付进行准备让用户支付

在需求中可能直接描述了一个订单的支付流程,而如果直接根据需求进行开发的话,就是咱们的第一版本的伪代码了。还是上文中所说的内容,这样的方式不是不可以,但是他的问题就在于对于后续不断迭代的过程是有挑战性的。

那么如果采用领域模型的设计是什么样的流程呢?根据上一小节的描述,我们应该先针对需求进行分析,然后根据需求中的内容进行领域建模。

那么从需求中我们可以看到:

根据需求中的名词,我们发现有订单、用户、用户地址、订单明细、商品

而虽然没有明说,但是我们根据客观规律以及产品沟通可以得到的关系有:一个订单对应一个用户、一个用户对应多个地址、一个订单对应一个地址、一个订单对应多个订单明细以及一个订单明细对应一个商品。

而根据支付的流程,我们还发现订单需要有下单、付款等功能,而用户需要有增删改等功能。

那么根据以上的分析,我们可以得到以下比较简单的模型:

那么根据这个领域模型,我们就可以将模型的行为抽出出来形成方法,而将方法的聚合组合为服务。而原来模型中的属性就可以转化为对应的值对象。也就可以形成了如下的设计:

我们可以看到,根据领域模型抽取之后的服务、对象的组合就比较像我们平时进行设计的值对象与服务的关系了。

支付的领域模型变更

那么有了第一版本的设计之后,我们得到了一部分完整的功能代码。那么我们现在有了新的需求:加入付款时的折扣功能:优惠价、限时折扣、原价。

根据前文的描述,我们应该先将需求的调整在领域模型上进行实现。那么针对付款的这个动作自然是在订单的付款()里进行调整。但是如果直接在付款的方法中进行调整的话,就变成了前文中的“问题设计”了。

在进行领域设计之前,我们首先要判断的是受到影响的元素之间的关系:“付款”与“折扣”。简单来看,折扣是在付款流程中执行的,所以折扣似乎应该放到付款里。但是依据单一职责原则(一个职责就是软件变化的原因),进行后续不同折扣功能以及付款功能调整的时候他们不应该互相影响,所以他们应该被拆解为不同的类。那么基于这个思想我们就可以将领域模型调整为以下形式(只画了订单部分的):

那么根据该模型进行项目的设计:

我们可以看到,对应在领域建模中的折扣接口,就指导了我们在项目设计的时候将其以“策略模式”进行实现,从而在增加不同折扣功能的同时,也保证各个模块满足单一职责原则。其他的需求变更本文就暂不展开。

最后

领域驱动设计是一种设计的思想,即便在不了解这种思想的时候,开发同学得出的方案也可能与其相同。但是领域驱动设计的意义在于,提供了一种“标准”的思维模式,让你可以将思考的流程标准化,而不是同时考虑过多问题。以本文中的例子为例,就是先考虑模型间的关系,再考虑针对模型的服务实现。当然,领域驱动设计在微服务

设计、数据库设计等方面也有不可忽视的指导性支撑,会在后续的文章中进行讨论。

了解一下DDD领域驱动设计相关推荐

  1. DDD领域驱动设计之聚合、实体、值对象

    关于具体需求,请看前面的博文:DDD领域驱动设计实践篇之如何提取模型,下面是具体的实体.聚合.值对象的代码,不想多说什么是实体.聚合等概念,相信理论的东西大家已经知晓了.本人对DDD表示好奇,没有在真 ...

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

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

  3. DDD 领域驱动设计:贫血模型、充血模型的深入解读!

    作者:JavaEdge在掘金 链接:https://juejin.cn/post/6917125801460629518 -     前言     - 要想深入掌握和了解 DDD 领域驱动设计的核心, ...

  4. 浅谈我对DDD领域驱动设计的理解

    从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...

  5. C#进阶系列——DDD领域驱动设计初探(五):AutoMapper使用

    前言:前篇搭建了下WCF的代码,就提到了DTO的概念,对于为什么要有这么一个DTO的对象,上章可能对于这点不太详尽,在此不厌其烦再来提提它的作用: 从安全上面考虑,领域Model都带有领域业务,让Cl ...

  6. DDD 领域驱动设计:贫血模型、充血模型的深入解读

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"书",获取 后台回复"k8s",可领取k8s资料 -     前言 ...

  7. [转]浅析DDD(领域驱动设计)

    最近在做一些微服务相关的设计,内容包括服务的划分,Restful API的设计等.其中比较棘手的就是Service的职责划分:如何抽象具有统一业务范畴的Model,使其模块化,又如何高度提炼并组合多模 ...

  8. 浅析DDD(领域驱动设计)

    最近在做一些微服务相关的设计,内容包括服务的划分,Restful API的设计等.其中比较棘手的就是Service的职责划分:如何抽象具有统一业务范畴的Model,使其模块化,又如何高度提炼并组合多模 ...

  9. DDD 领域驱动设计落地实践:六步拆解 DDD

    引言 相信通过前面几篇文章的介绍,大家对于 DDD 的相关理论以及实践的套路有了一定的理解,但是理解 DDD 理论和实践手段是一回事,能不能把这些理论知识实际应用到我们实际工作中又是另外一回事,因此本 ...

  10. DDD 领域驱动设计-三个问题思考实体和值对象(续)

    上一篇:DDD 领域驱动设计-三个问题思考实体和值对象 说实话,整理现在这一篇博文的想法,在上一篇发布出来的时候就有了,但到现在才动起笔来,而且写之前又反复读了上一篇博文的内容及评论,然后去收集资料, ...

最新文章

  1. Mysql for Mac 安装及环境配置
  2. 昵图网学校计算机教室制度,ClassIn在线教室
  3. 云原生乘风者联合征文活动——说出你和「阿里云云原生」的故事
  4. 用VSTS进行网站压力测试
  5. oracle sqlplus执行脚本_连接Oracle出错:sqlplus quot;/as sysdbaquot;
  6. JavaScript中Array类型方法总结
  7. 使用DxVcl为Python的飞信库写一个简单的GUI
  8. 如何为开源项目做市场
  9. 原生Js汉语拼音首字母匹配城市名
  10. 史上最全SpringCloud2.0视频教程
  11. 浅谈批处理for命令中的变量扩展
  12. 如何解决“Appstore无法下载软件”的问题
  13. HTML 网页特殊符号代码大全
  14. 就业指导期末试题(含正确答案)
  15. 品味kettle--(一)用eclipse 搭建源码
  16. 如何编辑小红书种草文案?小红书怎么编写种草文案?
  17. SAP 物料可用性检查
  18. 结构体类型数据单链表的操作
  19. Google 手机地图-中国版~
  20. (转)VC++之系统控制之设置显示系统当前时间

热门文章

  1. Python使用Reportlab处理PDF数据 - 创建特殊功能
  2. 美国CSTA K-12计算机科学标准「中文版」转
  3. c语言pi算法程序,C语言计算圆周率PI
  4. 海信电视部分聚系列服务器存在异常,海信液晶电视常见故障及维修
  5. 基于马尔萨斯的人口模型的一个Logistic模型(MATLAB)
  6. Ubuntu 9.04安装永中Office 2009
  7. html5网页设计作业代码 大学生校园网站制作 学校官网制作html
  8. 对付U盘病毒彻底免疫
  9. asp解决“另一个SqlParameterCollection中已包含SqlParameter”的方法
  10. 中标麒麟linux系统安装打印机_中标麒麟系统安装教程