查看原文
其他

你的医书是假的!批评付施威的《DDD诊所——聚合过大综合症》

潘加宇 UMLChina 2024-03-10
DDD领域驱动设计批评文集
“软件方法建模师”不再考查基础题
《软件方法》各章合集
一、说在前面
这两天在“ Thoughtworks洞见”公众号上看到一篇付施威的《DDD诊所——聚合过大综合症》
相对于DDD圈子各种各样错误百出的玄学文章,这篇《DDD诊所》已经算是想要认真探讨问题的难得之作,说得粗俗一点——可以算是翔中的豆芽菜或金针菇了。
如果仅仅是自娱自乐的学习心得,只要不自吹“创新”、“棒棒哒”或被人热捧为“创新”、“棒棒哒”,我是不会去批评的-参见《“以炮换马”的DDD歪招是否可以作为起步》
但这里作者要去当医生治疗病人了(虽然不收诊金),那就得好好说一说了。
其实我要批评的几点,在之前我写的《DDD领域驱动设计批评文集》序列文章中都提过(因此本文我会偷懒,放上大量我写过的文章的内容截图),这篇文章也不幸一一命中,这并非偶然。
从我开始写《DDD领域驱动设计批评-合集》序列文章,到现在已经有4年了,在这期间:
*没有人针对我写的文章内容做出反驳;
*有个别DDD粉丝嫌我气量狭窄,《潘老师,你的气度有点小了!》
*有个别DDD圈子人士质疑我的动机,《批DDD不批马保国,说白了就是DDD影响生意了》《回应张逸老师(一)圈子文化》
*没有DDD圈子人士当面(包括线下和线上)对我恶语相向,都是很礼貌地叫我“潘老师”;
*【我推测】大多数DDD圈子人士没有稍为认真地扫一遍我写的文章;
*少数DDD圈子人士表现出很有诚意和我讨论和向我学习建模。我说,如果乐意学习或和我讨论,可以先看书和做题,我出了几百道扫码自测题,做对大半后,如果有问题可以再讨论——理由参见:
《软件方法》分步改进指南
《软件方法》自测题为什么不直接给出答案
漫画版《软件方法》、奶头乐和高数买菜
*但是(往往真正要说的藏在这里)!目前为止,我还没有发现DDD圈子有人按照我的建议认真去做——这个我看得出来的。
我描述的上述若干现象,看起来有点诡异甚至互相矛盾,对吧?可以看我写的《小甜甜和牛夫人?》,理解了这个圈子的创新观和风气,就不觉得诡异了。
(可能有的读者会说,你自我感觉怎么这么好?大家都很忙,你阿猫阿狗随便写点什么,人家凭什么要认真看,认真学习或认真反驳?问题在于,至少在这一点点上,我不是阿猫阿狗啊。毕竟圈子吹捧的这个东西,我还是最早引进的人之一,参见《小甜甜和牛夫人?》
**********
下面,我按照《DDD诊所》的文章结构依次评点,其中最严厉的批评出现在【治疗建议】部分。
二、【患者主诉】评点
《DDD诊所》的这部分罗列了系统的一些功能以及存在的问题。
(1)罗列功能可以加上(或改成)业务序列图。
以下例子可供参考。
2022年,《DDD诊所》作者所在公司Thoughtworks的另一位同事曾来上我的课,并提供了一个“企业持续交付流水线平台”的案例项目在课上剖析,现在又看到《DDD诊所》这篇文章里也提到类似项目,不知道两者之间有没有关系。
以下几张图就是当时我在课上和学员讨论后针对“企业持续交付流水线平台”案例画的第一个迭代的现状业务序列图、改进后业务序列图和系统用例图。

 

图1 现状业务序列图

 

图2 改进后的业务序列图

图3 系统用例图
以上图仅在课上短时间内完成,仅供参考。
(2)类图很可能逃避了最难也最有价值的规则部分
接下来,《DDD诊所》给出了一张类图:

图4 《DDD诊所》中的类图
能认认真真画一张类图,光是这一点就已经压倒很多DDD文章。
很多DDD文章列出两三个类,甚至有的就一个类,然后就开始往上批量刷**Service、**Repository。《DDD诊所》所给出的类图中,类的个数远远超越了那些文章,而且我仔细看了类图,表示法上也基本没有什么问题。
可能存在的问题是内容的问题:规则没有显式建模。
类图右侧的“触发规则”、“阶段质量门禁项”的比较规则,这些“规则”不是独立的,它们隐藏在类以及关系之中,应该在类图上显式表达。
很多人经常犯的错误就是,轻飘飘地用一个类“**规则”搞定,似乎这样做之后“**规则”就会从天上掉下来。这其实是逃避了问题,因为封装“**规则”相关的知识,很可能就是你的系统存在的理由。
(如果“**规则”里面的逻辑已经由第三方搞定,那么必定还会有另外一个“**规则”的逻辑是你的系统存在的理由,钱哪里是那么好挣的?)
如果一个类的名称是以下面这些结尾:er、or、器、策略、Strategy、Policy、规则、Rule、算法、Algorithm……一定要警惕,有可能这种类是逃避了真正问题的废话。
关于这个内容,我之前的文章《软件开发团队的脓包》里面的“废话迷”部分可以参考:

图5 《软件开发团队的脓包》的“废话迷”部分截图
我写的《软件方法》下册第8章也可以参考:

图6 《软件方法》第8章截图
上面提到的“企业持续交付流水线平台”,我当时在课上画过一个类图,也可以作为参考。灰色部分表达了规则。

图7 我在课上画的类图
规则表达清楚了,剩下的就相对简单了,无非是在规则上玩游戏,把结果记录下来。不过,我们看到的很多建模者,能力仅能达到“把结果记录下来”,很难做到“显式描述规则”。
而“显式描述规则”的缺失,往往会带来另一个错误:事物规格和事物不分。
例如,图4中,“阶段”、“步骤”中的“参数”指的是什么?是名称还是值?
项目的哪一个阶段(步骤)应该有什么样的参数?这是一个知识。
某个具体项目的某个阶段(步骤)什么时间发生的,具体参数值是多少?这是另一个知识。
这两个知识变化频率不一样,不能用一个概念覆盖。
还有像这个:

图8 《DDD诊所》中的经“医生”诊断后的类图
图中这样一写“下一个步骤是阶段的步骤列表中的下一个”就完了?然后好事就从天上掉下来了?步骤的顺序在哪里呢?这可不是在“步骤”中加个“序号”属性或者在“步骤”上建一个自反关联就能解决的。
一个阶段有哪些步骤?它们之间的顺序如何?这是一个知识。
张三的项目当前到了某某阶段的某某步骤,这是另一个知识。
这两个知识不分开,必定会导致大量冗余的数据(即对象的属性值)。
三、【诊断】评点
这一部分描述了使用聚合/组合时要警惕的问题。这个部分的问题主要是造词,“***综合症”之类。
另外,“聚合根”就是一个伪创新,这个内容我在《DDD话语“聚合”中的伪创新》中已经写过,不是本文批评的重点。
四、【治疗建议】评点
这是本文批评的重点。
(1)《DDD诊所》引用的文字存在严重的翻译错误。
为了“治疗”,《DDD诊所》先摘了一段《领域驱动设计》中译本的文字:

图9 《DDD诊所》截图
可以看出,《DDD诊所》的作者所摘的文字应该来自人民邮电出版社2016年的译本。
这个译本翻译得很糟糕!
《DDD诊所》所摘的内容,原文并不是在一起的,而是分离的两段话。我在文章《猴子掰玉米?比较不同版《领域驱动设计》说“不变式”和“聚合”》中针对第二段译文做了评价。

图10 《猴子掰玉米?比较不同版《领域驱动设计》说“不变式”和“聚合”》截图
图10中的重点是:不变式是类的不变式,是类所封装的逻辑,而不是说有个孤立的“不变式”,然后几个类去满足它。
而这一点,《DDD诊所》的作者应该不了解,例如,他在文章里说“订单与订单行之间存在固定规则”:

图11 《DDD诊所》截图
有趣的是,《DDD诊所》的作者还引了维基百科作为佐证:

图12 《DDD诊所》所引用的维基百科
问题是,我们讨论的是面向对象语境下的不变式(部分历史可参见《“**领域驱动设计”的这个不变式是不是多余》),面向对象语境下,所有的逻辑都封装在类里面。
《DDD诊所》里用的方法应该是(或者自以为是)面向对象方法吧?如果不是面向对象,而是作者刚创新的“领域驱动设计大模型人工智能数智化敏捷方法”,那以下文字就当我没说。
不变式所表达的本来就是一段规则,你爱怎么组织都可以。面向对象的组织,就是把这些规则按类组织,从而把逻辑封装在类中。
例如,越南某公司有这么一些规则:
员工年龄不得超过性别的年龄上限,例如男性45,女性50。
还有,部门中男员工人数不得超过女员工人数的2倍。
可以分为两个类的不变式:

图13 “员工”的不变式和“部门”的不变式
可以看到,不变式是放在类上面的,并不是像图8、图16和图18一样,放在类关系上。
我们还可以观察到,“员工”的直接或间接属性有:年龄、员工性别.年龄上限、员工性别.名称。
要是这些属性的值的组合没有任何约束(例如年龄46,性别男),那么系统的复杂度会大大降低——可惜,现实中不是这样(这正是图10中Bjarne Stroustrup所强调的)。
“员工”的不变式正是这些属性之间的约束的一个表达式——并不是像图8那样意淫式地随便写句话;
(不变式如何表达,参见《不变式有标准或者语法吗》
同样,“部门”的直接和间接属性,除了“员工”集合之外,还包括上面列出的“员工”的直接或间接属性,而“部门”的不变式正是这些属性之间的约束的一个表达式——并不是像图8那样意淫式地随便写句话。
那可不可以把“员工”和“部门”的关联方向倒过来?可以的。这时,原来加在“员工”上的不变式应该不用修改,但原来加在“部门”上的不变式就要挪到员工上(因为“部门”访问不到刚才那些属性了),而且表达式也要修改——肯定能表达,无非是集合运算,区别在于更直接还是更弯弯绕而已。
从这一点也可以看出,到底是“员工”知道“部门”合适,还是“部门”知道“员工”更合适?不变式可以作为参考依据之一。

要提醒的是,不变式仅仅是表达逻辑的选择之一,而且也仅能表达部分逻辑。

DDD圈子为什么独独吹捧这个呢?哦,因为《领域驱动设计》提到了它,更关键的是,这个圈子是一个封闭的互吹互捧的圈子。

(2)问题更大的是,《DDD诊所》的作者对译文的糟糕没有敏感性。
最简单的一点,Invariant译为“固定规则”的,【我见过的】是独此一家!
列举几本有一定水平的书(说的是原著),有译作“不变式”的:

图14 Meilir Page-Jones《UML面向对象设计基础》(人民邮电出版社)截图
有译作“不变量”的:

图15 Ian Graham《面向对象方法原理与实践(原书第3版)》(机械工业出版社)截图
以上所列,都不是UMLChina译,以免被认为故意挑选。
《DDD诊所》的作者居然不觉得有违和感,是因为只看过《领域驱动设计》的这个译本,而没有看过其他书?
如果《DDD诊所》的作者手上资源有限,例如,没有别的译本,没有英文原文,没看过其他书……那应该想办法去丰富自己的资源,等学习好了再给人看病。
当然,如果创新观和我在《小甜甜和牛夫人?》中评论的创新观一样,那我就不再对此说什么了。
(3)问题最大的,《DDD诊所》的作者缺乏对“废话”的基本敏感。
不了解或误解(1)中我说的不变式的知识,这个好说,花时间学习就是。
下面这个才是我认为最严重的:

图16 《DDD诊所》截图
《DDD诊所》的作者把“A更新的时候,会不会引起B的某个属性的更新”,“如果两个实体暂时不一致,是否会导致难以承受的业务后果”、“订单的总价等于订单行的总价之和”看作不变式要表达的内容。
——这是不变式吗?这是冗余吧?
像图中“订单的总价等于订单行的总价之和”,如果这个公式被严格遵守,那么“订单”类的“总价”属性在逻辑上就是冗余的,应该删掉。
既然“订单”的“总价”属性没了,图16上所标的不变式也就不存在了。
那怎样才是“订单”需要表达的不变式呢,我举下面的例子供参考:

图17 合理的不变式
“订单”类中不变式的意思是:
任何一个订单对象,其订单项集合中不存在这样的订单项,本订单的配送地址所属区域就在该订单项所购商品的禁售区域内。
例如,某款“iPhone**”不能卖往长三角地区,如果订单的配送地址是“南京市****”,那么订单里面是不能存在购买“iPhone**”的订单项的。
注意,我给出的图13、图17等类图,上面的信息没有冗余。
**********
面对冗余,建模者毫无察觉,反而甘之如饴,这说明建模者缺乏基本的建模训练,这已经和面向对象或不变式没关系,即使辩解说“我用的不是面向对象,用的是领域驱动设计大模型人工智能数智化敏捷方法”也一样的。
我在另一篇文章《“**领域驱动设计”的这个不变式是不是多余》中也批评了类似的“不变式”废话:

图18 《“**领域驱动设计”的这个不变式是不是多余》截图

难怪,我一直“赞扬”很多DDD伪创新:投资少、见效快、产量高、门槛低、仪式感十足,它们确实迎合了某些开发人员的需要。

很可能有人会以“性能”为理由为自己的能力缺乏辩护。
是的,如果在实现时确实由于需要频繁计算订单总价,导致出现不可调和的性能问题,可以添加上冗余属性(用数据库语言就是冗余字段)“总价”,甚至添加冗余类(用数据库语言就是冗余表),但这属于通过添加冗余来缓解性能问题的实现套路,和具体的领域知识无关。
关于“性能”这块遮羞布,可以参考《软件方法》中的内容。

图19 《软件方法》“性能”截图1

图20 《软件方法》“性能”截图2

[架构师强化]8月28-9月1晚8点分析设计高阶网络公开课(原“剔除伪创新的领域驱动设计”)
[EA-029/石油钻井管理平台]35套UML/SysML+EA/StarUML的建模示范视频-全程字幕
如何选择UMLChina服务
作者微信:umlchina2
继续滑动看下一个

你的医书是假的!批评付施威的《DDD诊所——聚合过大综合症》

潘加宇 UMLChina
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存