2019独角兽企业重金招聘Python工程师标准>>>

DDD虽然很火但是理论实在过于枯燥,很多人在软件开发的焦油坑中被推荐了DDD,以为终于找到一种解救自己的办法,但是却很快迷失在无数的概念中。通用语言,一种UML的升级语言吗?贫血模型,你居然说我用了10多年的模型是贫血的?领域服务,和我定义的Service有什么区别?聚合根、限定上下文、CQRS、Event Source,有必要搞这么复杂吗?于是很多人在没有理解DDD之前就放弃了——他们渴望得到一把屠龙刀能快刀斩乱麻地解决问题,然而却只得到一本晦涩难懂故弄玄虚的武功秘籍。

本文基于个人对于DDD的理解,尽量避开DDD枯燥的理论知识,希望通过本文能让读者对DDD是什么有个简单的认识。本文不能指导你去实施DDD,只能是一篇基本概念的扫盲,限于本人水平所限,对DDD理解难免有误,请抱有怀疑精神阅读。完整的DDD理论请阅读专门的DDD著作。

软件开发背景

DDD2004年就被提出来了, 为什么最近却越来越火,在国外已经成了开发标准。我觉得根本原因是,现在软件开发正变得越来越复杂。虽然随着技术的发展,一些原来实现起来困难的事情有了简单的技术解决方案,但是人类对于极致体验的需求,不断对软件设计提出挑战,于是新的问题又不断被提出来。比如,曾经在手机上播放32和弦音乐是一件技术含量很高的事情,现在几乎不需要任何技术成本就能实现,但人对于手机的需求早已经不在这个层次上了,人们对技术提出了新的要求。从某个方面讲, 技术永远落后人的需求,这是促成技术发展的原因。更快的速度,更好的体验,更贴心的功能,也正因为如此,软件变得更复杂。

开源技术的发展,极大简化了软件的开发, 很难想象如果没有开源社区, 现在开发软件会是一种什么样的体验。但是与此同时我发现, 开源社区很擅长解决通用的技术问题, 比如C10k问题、一致性问题、大数据问题、微服务部署问题等,这些技术与特定业务无关具有普遍的适用性,容易做成通用的解决方案,同时也是云计算厂商的热情所在。但是在软件的复杂度方面,我们的社区似乎很少讨论。软件功能越来越复杂,软件迭代速度越来越快,软件复杂度越来越高。我看到的一个事实是, 随着开源技术的发展, PHP程序员在大流量、高性能技术方面已经有了长足的进步,但在开发复杂业务软件方面的能力,进步并不明显。

我认为原因是缺少社区氛围:

  1. 在很多成熟公司,PHP只是作为一种数据处理层存在,给前端格式化数据用,复杂的业务逻辑都交给其他后端语言了
  2. 在创业公司中,大多数PHP项目的复杂度并不高或者在复杂度变高之前项目就已经死掉

所以, 当你在一个以PHP为主要语言的公司,且业务复杂度变得越来越高时,问题就出现了。

业务逻辑会变得越来越复杂,但是通用的开源技术对此帮助不大

事务脚本开发

我们先试图分析下软件越来越复杂的原因。我们最习惯的一种业务开发方式是: 定义各种Service封装业务逻辑,我们称为业务逻辑层。

有问题吗?在业务逻辑变得很复杂之前没有问题,甚至可以说这是一种很好的方法,但在业务复杂度变高后,可能会出现一些复杂度不受控制的问题。这种开发方式被叫做事务脚本的开发方式。

下面我们从一个产品需求的变更看这种方式的局限性。

在前期,我们分析产品需求,按照产品需求设计出流程, 流程被设计到各种方法里, 每个方法看起来简单自然,每个方法只做一件事情,一切都很好。

随着时间推进,流程变得复杂,我们不得不拆解这些功能,拆解的原因有很多:

  1. 为了代码重用
  2. 为了方法可读性
  3. 仅仅是个人习惯

如果复杂度停留在这一个阶段, 情况还不算糟糕, 事实上我们很多项目复杂度就停在这个阶段。

时间再推进,慢慢出现的一些问题:

  1. 这个功能可以用到原来流程的A,但是又有一点小区别,那我是再写一个还是加个参数处理下呢?
  2. 我之前拆解的功能B,现在看来好像有点问题,我想调整下但是会不会影响别的功能呢,如果影响是再写一个还是加判断处理呢?
  3. 这个方法就是我要的功能, 但是它有一次发短信操作,我加个判断把发短信屏蔽下吧

软件复杂度变得越来越高后,有很多因素会诱惑你加快复杂度的增加:

  1. 这个功能比较着急,但是我不确定改了会不会影响别的地方,复制改下吧
  2. 这个方法里面这些判断是做什么的,不管了,复制下吧
  3. 这个功能单元测试明明OK怎么不行呢, 我擦里面怎么多了个判断, 这个分支没覆盖到!

软件复杂度的增加不是线性的,随着时间的推移,如果缺乏有效的管理,复杂度会急剧增加

事务脚本的特点:

  1. 开发简单,过程式的代码,容易开发,但是复杂度容易失控
  2. 过程式,即使你定义了class,也不能否认它过程式的本质,过程式本来没有问题,因为大多数web流程就是过程式的:取几个数据,格式化一下,再加个缓存。
  3. 事务脚本的方法可以相互调用,无层次依赖,最终形成一种复杂的网状调用关系
  4. 业务逻辑分散在各处,被实现为各种不同的功能方法, 这些方法并不知道自己属于哪个业务
  5. 业务逻辑,存储层逻辑,外部服务调用混杂在一起

事务脚本大多数情况下是一种很好的开发方式,特别是对于以读为主写逻辑不是很复杂的应用

领域驱动开发

但是,事情总有变得复杂的时候。这个时候,我们需要有一种方法,可以很好的控制软件复杂度的增长,领域驱动开发就是这样一种方法。关于领域驱动的知识网上有很多, 简单说DDD是解决复杂中大型软件的一套行之有效方式,在国外已经成为主流。DDD认为很多原因造成软件的复杂性,我们不可能避免这些复杂性,能做的是对复杂的问题进行控制。而一个好的领域模型是控制复杂问题的关键。

DDD给程序员带来的一个最大改变是思维习惯的改变。

事务脚本开发方式,优先考虑的是数据和行为, 设计好数据库,然后按照业务流程用各种方法把数据存下来。DDD接触到需求第一步就是考虑领域模型,而不是将其切割成数据和行为,然后数据用数据库实现,行为使用服务实现,最后造成需求的首肢分离。DDD让你首先考虑的是业务语言,而不是数据。重点不同导致编程世界观不同。

举个具体的例子:产品需求是实现一场比武。

事务脚本开发方式:

  1. 定义一个比武的方法
  2. 产品需求是两人比武,参数A和B
  3. A使出降龙十八掌, 封装一个方法
  4. B防守
  5. B进攻
  6. ....
  7. 结束

领域驱动开发方式:

  1. 这是做什么? 比武,这是一个比武的业务领域
  2. 这个领域有什么?武林高手,每个武林高手有自己的武功特点。武林高手就是领域中的实体,因为有多个武林高手,那我们可以定义一个武林高手的抽象类或者接口, 然后实现乔峰,段誉等具体子类, 每个子类有自己的武功特点。
  3. 比武是一个具体动作, 放在哪个实体都不合适,定义成领域服务。

在事务脚本开发方式中,直接开发业务流程。在DDD开发中,我们是定义一个比武模型, 模型里有各种武林高手, 他们在一个舞台上比武。随着业务逻辑发展, 新的武林高手、多人比武等新的比武方式被加进来,事务脚本开发的方式会被添加越来越多的方法,这些方法被平铺在事务脚本里,被复制粘贴,被修改扩展,最终导致复杂度不可避免的增加。在DDD模型中,我们使用建立了一些关于比武的领域模型, 各种高手被有序封装在各种特定实体里,他们在领域服务的协助下完成业务动作。添加新的武林高手或者新的比武方式,并不会对领域模型产生冲击,最终复杂度被控制在一定范围内。

DDD接触到需求第一步就是考虑领域模型,一个好的领域模型是控制复杂问题的关键。看到领域模型代码,就看到业务需求,没有翻译没有转换,保证软件真正实现“拷贝不走样”。

领域驱动设计常见错误

数据实体

数据实体在领域驱动里叫贫血模型,就是只有数据没有行为的实体。数据实体对数据对象提供一层抽象,隔离数据库和业务逻辑,在JAVA领域曾经大行其道。这种方式的问题是:

  1. 首先,实体的抽象出发点不是构建业务模型,可能仅仅是为了屏蔽数据库细节,实体的有效性是个问题
  2. 其次,由于实体没有行为,行为代码仍然被分散在事务脚本里, 没有被按照领域模型组织起来,复杂度风险仍然存在

无领域模型

代码里有实体层,有领域服务层等看似非常DDD的组件,但是实际上这些实体和领域模型没有任何关系,程序员完全不理解业务模型的存在,这只是一种新的代码组织方式,并不理解领域驱动的真正意义。由于没有模型, 代码依然被分散在各个实体或者领域服务中,也无法灵活应对需求变化。

DDDLite

代码反应了领域模型,但是这个模型是开发理解的,缺乏和领域专家持续沟通。这种情况下的领域模型正确性存在风险, 随着时间推移, 这个风险会越来越大,DDD的作用会大大降低。

领域驱动是一种设计模式

领域驱动不是一种设计模式, 是一种软件开发方法,但是在领域驱动的实现中,会需要用到很多设计模式。设计模式来源于建筑学,把一些常见的设计处理方法加以抽象、分类和命名,成了一个个设计模式。每个设计模式有特定的适用场景。领域驱动设计是一种软件分析和设计的方法,当业务模型被设计出来后, 模型的实现可能需要用到各种设计模式。

领域驱动很复杂

我觉得主要是思维的转变,尝试定义领域模型来解决问题,这需要一个思维转变过程,有时候这会很难。

我应该立刻采取领域驱动

多数情况下,领域驱动都不是我们开发的首选方式。 因为我们对于业务的理解需要时间,而且多数情况下,我们业务逻辑并不复杂。如果你对你的代码复杂度现状满意,那就暂时不用去考虑。具体可以参见DDD评分卡,一个评估你是否需要采取DDD的计分卡。

领域驱动的一些收获

  1. 软件是复杂的,而且会越来越复杂,避免软件复杂度是不可能的, 但是我们可以控制软件复杂度
  2. 软件复杂度分为事实复杂度和随机复杂度, 前者是软件事实存在的,后者是软件开发不断迭代带来的,是可以有效控制的
  3. 软件开发最重要的技能是抽象。抽象出软件中稳定相对不可变的部分重点优化和测试,把可变部分通过参数化和配置化,从而最大程度的增加软件适应性。领域模型就是软件中相对不可变部分
  4. 领域驱动并不适用所以场景,你可以把它理解为一种复杂度管理方法,如果软件复杂度不高, 使用DDD的收益不大,当然对于业务模型的思考对于软件的扩展性是有益的(可参见DDD评分卡)
  5. 准确的领域模型是DDD的关键,但是这往往不容易,不要指望一上来就发现正确的模型
  6. 通用语言不是技术语言,不是软件技术,是一种和领域专家约定的词汇表,只要你和领域专家能就领域模型无障碍沟通,就是好的通用语言。 领域专家包括产品,运营和老板等擅长此领域的人。
  7. 开发人员容易低估业务模型的价值,模型能保证实现和需求是一致的,在模型的提炼过程中,开发能对业务更清晰地认识并发现产品需求中不清晰的地方,不要指望产品能给出一个完美的需求。
  8. 文档、注释等只能辅助理解业务,一方面保持文档的有效性是一件困难且代价较大的事情;另一方面强调文档而忽略代码本身的复杂性是一种本末倒置的做法。
  9. 重构能解决一部分问题,但是如果你复杂度不被管理,下一次重构很快又会到来,而且随着业务发展,重构的代价会越来越大。
  10. 微服务的切分和领域的边界的息息相关的,这也是国外谈论微服务基本会谈到DDD的原因,国内似乎不是这样。

愿你的DDD之旅一切顺利!

转载于:https://my.oschina.net/rssidea/blog/1529128

谈谈PHP系统中的领域驱动开发相关推荐

  1. 谈谈微服务架构中的领域驱动设计

    谈谈微服务架构中的领域驱动设计 https://mp.weixin.qq.com/s/43HSud6ijdVzPA_wdLrxzQ 谈谈微服务架构中的领域驱动设计 本文是关于领域驱动设计与微服务架构结 ...

  2. 从零开始使用CodeArt实践最佳领域驱动开发(三)

    5.领域模型设计 在开始考虑如何构建账户子系统的领域模型之前,我们先来看看关于CA里领域模型的基本概念.初次接触这些陌生的概念确实会一知半解,不过没有关系,大家实践几次领域设计后就会融会贯通,深刻体会 ...

  3. 领域驱动开发(domain driven development)

    链接: https://www.zhihu.com/question/56332619/answer/250971065 什么是领域驱动开发 将问题抽象为一个领域解决方案.并针对此领域(即抽象)进行开 ...

  4. 简单的11步在Laravel中实现测试驱动开发

    测试驱动开发(英语:Test-driven development,缩写为TDD)是一种软件开发过程中的应用方法,由极限编程中倡导,以其倡导先写测试程序,然后编码实现其功能得名. 下文是我在Mediu ...

  5. Android系统 linux内核按键驱动开发

    Android系统 linux内核按键驱动开发 前言 刚入门的小白,在csdn的帮助下完成了第一个按键驱动,特写此文记录学习并分享给有需要的人. 1.修改设备树.dts 我是用的开发板是rp-rk32 ...

  6. android属于数据库管理系统,详细谈谈Android系统中的SQLite数据库的应用

    数据库是按照数据结构来组织.存储和管理数据的仓库,而在信息话的社会,数据库又不单单仅限与数据的相关内容,现在数据库技术是管理信息系统.办公自动化系统.决策支持系统等各类信息系统的核心部分,而SQL是结 ...

  7. 在windows系统中安装显卡驱动

    Windows系统下在docker中使用nvidia的GPU 微软官方文档 https://docs.microsoft.com/ja-jp/windows/ai/directml/gpu-cuda- ...

  8. 在 Windows10 系统中安装 Homestead 本地开发环境

    在 windows10 系统中安装 homestead 本地开发环境 在 windows10 环境下安装 homestead 开发环境,网上有很多相关教程其中大多都是 mac 环境,很多大神都是用户的 ...

  9. Ubuntu系统中查看电脑驱动信息

    Ubuntu系统中查看电脑驱动信息 sudo ubuntu-drivers devices 俺的电脑显示以下信息 俺有一块6GB的GTX1060显卡 hello@hello-XPS-8930:~$ W ...

  10. Ubuntu16.04有线网络无法连接,原来是网卡的型号和系统中网卡的驱动不匹配

    Ubuntu16.04有线网络无法连接,问题解决,原来是网卡的型号和系统中网卡的驱动不匹配 一假期回来,打开电脑有线网络突然连接不上了,哭.... 参考网址:http://events.jianshu ...

最新文章

  1. 利用反射实现类的动态加载
  2. How to stop worrying & start living
  3. 浏览器事件循环与node事件循环
  4. centos下安装apache+mysql5.7.13+php5.3.3+phpmyadmin4.0.10
  5. hdu 4280 最大流sap
  6. 清华博导:我有个好学生想放弃科研 去中学当老师
  7. 界面设计方法(2)— 5.功能按钮设计(新增,查询)
  8. __stdcall函数调用约定
  9. mybatis 二级缓存失效_给我五分钟,带你彻底掌握MyBatis的缓存工作原理
  10. Python教材(数据分析、数据挖掘与可视化)——第7章课后习题
  11. 移动Web开发之流式布局笔记
  12. Wet Shark and Flowers(思维)
  13. 2022/2023届-Matlab数字图像处理—选题推荐
  14. 电视/电视盒点播APP软件系统定制开发方案
  15. ios 定位权限获取
  16. conda安装本地whl文件
  17. java技术分享些什么,大牛最佳总结
  18. 京东返利PHP采集关键字,PHP实现京东API的授权HASH算法
  19. oracle为什么主键不唯一,Oracle GoldenGate 针对表没有主键或唯一索引的解决方案
  20. 在图形用户登录界面输入正确用户名与密码后,闪了一下(即将要登录进去那一刹那),它NND给我蹦出来了!

热门文章

  1. 使用用VMware Workstation   实现DNS服务器之间的委派和区域传送
  2. zabbix企业应用之low level discovery监控memcache
  3. 通过pxe远程安装linux,通过PXE远程安装Linux系统
  4. pjsip workshop
  5. android 注册静态广播接收器VS注册动态广播接收器
  6. Java中的Arrays类使用详解
  7. 查找所选灯笼数(查找第二大)
  8. 51Nod1019----归并排序(递归)
  9. 仿生软体机器人就业咋样_SRT近亿元B轮融资,中国软体机器人技术从空白到全球领先...
  10. 计算机主机英语怎么说,电脑的英文-电脑的主机这个词英语怎么说?电脑的主 – 手机爱问...