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

DDD领域驱动设计批评文集

“软件方法建模师”不再考查基础题

《软件方法》各章合集

我写了一篇文章,批评付施威的《DDD诊所——聚合过大综合症》(以下简称《DDD诊所》),文章是《你的医书是假的!批评付施威的《DDD诊所——聚合过大综合症》》(以下简称《你的医书是假的》)。

一、《你的医书是假的》关键内容

《DDD》诊所说,下面这个是不变式:

图片

图1 《DDD诊所》截图

我在《你的医书是假的》中提出以下批评:

(1)不变式是类的不变式。《DDD诊所》作者把它放在关系上是不对的,说明该作者没有理解不变式。

(2)《DDD诊所》作者引用的《领域驱动设计》译文的翻译很糟糕(参见《猴子掰玉米?比较不同版《领域驱动设计》说“不变式”和“聚合”》),关键内容的意思译反了,而《DDD诊所》作者并无察觉,反而整段引用作为依据。

(3)不变式不是用来描述像图中的“订单总价=sum(订单行总价)”这样的内容的。如果这样的约束被严格遵守,说明“订单”的“总价”属性在逻辑上是冗余的,应该删掉。既然“总价”属性没了,图1的所谓“不变式”自然也就没有了。

也就是说,改变订单属性值(包括加/删订单行以及其他操作)的时候,不需要判断某个含有“总价”属性的表达式是否为真——实际上也没法判断,“总价”属性都没有了嘛。

接下来,我再针对图1做进一步的阐述,帮助进一步理解不变式,并评点在《你的医书是假的》文章下面的留言。

二、等号和赋值号没搞混吧?

不变式是一个布尔表达式。

我在这里提这一点,到底是什么意思呢?

意思是:

如果发现有“不变式”表示为相等关系的表达式,那就要警惕一下,建模人员是不是把“=”当成赋值来理解了。

例如,图1的式子“订单总价=sum(订单行总价)”,有可能就会被误解成“把订单项的价钱之和赋值给订单总价以同步这两边的值”。

读者可以注意到,我在《你的医书是假的》画的图例中,没有相等关系的不变式。

读者还可以对比下面的例子: 

图片

图2 三角形及其不变式

这个还行,对吧?

那读者再看这个:

图片

图3 错例:等边三角形及其不变式?

这个是不是比较诡异?不过,这样的图倒是符合DDD伪创新的口味:投资少、见效快、门槛低、产量高、仪式感十足。

注意,我在图3中用“==”表示相等关系,而不是用“=”。其实这是违反OCL语法的,OCL里就是“=”表示相等关系,和SQL类似,但考虑到上面说的赋值误解(特别是显示在类图上时),我倒是觉得OCL应该改一下。

那么,可不可以有相等关系的不变式?可以有,但很少。相等关系表达式,经常是出现在前置条件和后置条件,而不是不变式。

什么时候可以有相等关系的不变式,这个就涉及到相当多的知识,不是本文能装得下的了,感兴趣的读者可以关注我的《软件方法》。

三、“订单总价=sum(订单行总价)”的另一个问题

这个问题在《你的医书是假的》中没指出来,在这里补上:

“订单总价=sum(订单行总价)”这个所谓的“不变式”,和图1中的属性名、关联角色名对不上。

图1中的属性名、关联角色名没有“订单总价”和“订单行总价”,只有“订单.总价”、“订单行.总价”——这也说明《DDD诊所》作者当时没有理解不变式是干什么用的。

四、不变式在哪里实现?

上面说了,不变式是一个布尔表达式,它只是说,对象的属性值不能违反这个约束,至于如何保证这一点,它没有做任何指定或暗示。

例如,把每一个不变式封装成一个方法,如果某个操作会修改某不变式中所涉及到属性的值,就调用相应方法来检查——这个工作可不轻松。

如果所用的实现技术支持前置条件、后置条件、不变式等契约编程,例如.NET中提供的Contract类,使用该类提供的服务即可。

五、类似“sum(订单行总价)”的运算会出现在哪里?

可能会有人问这样的问题。

类似“sum(订单行总价)”的运算,像其他针对各个属性的各种运算一样,并没有什么特别,会出现在类的某个操作或多个操作里。

什么操作?正确的回答是“信息不足,不知道”,可能“订单”专门有一个“计算总价”的操作,“sum(订单行总价)”藏在该操作的实现中,也可能“订单”根本不需要有“计算总价”的操作,“sum(订单行总价)”藏在“订单”的某个更复杂的操作的实现中。

不管怎么样,这和图1那个相等关系的“不变式”不存在什么有规律的对应关系。

六、关于付施威的解释

《DDD诊所》作者付施威在《你的医书是假的》的评论区留言解释:

图片

图4 《DDD诊所》作者付施威的解释

先说解释中的第3点:

如果还有“订单总价<3000”这样的约束,之前的结论也没有变化,只不过“订单”的不变式多了一个: 

图片

图5 加上订单总价<3000后的类图变化(注:我把类名和属性名改成订单项.价钱)

再说解释中的第1点:

付施威谦虚地解释“DDD诊所是内部活动”、“主要职责是被人研究”,但下面的海报传递出来的信息似乎是不一样的。 

图片

图6 活动海报1

图片

图7 活动海报2

开直播以专家身份传道授业解惑没问题,但被指出问题了,又谦虚地说是学习体会,这个似乎就不太好。

七、关于林宁的评论

付施威的同事林宁在《你的医书是假的》下留言:

图片

图8 林宁在《你的医书是假的》评论区的留言

以下的观点并非只针对林宁的评论,而是针对近年DDD圈子的典型反馈——“潘老师说得对,但是%……&¥&**”。

并没有恶语相向,而是潘老师说的没问题,我们也没问题,大家都Happy,这不是挺好吗?

我在《潘老师,你的气度有点小了!》阐述了一些我的看法,本文不再涉及该文中提到的内容。

本文重点展开谈一谈林宁所说的“一定程度的冗余”。

(1)问题①②③

问题①

假设只考虑领域逻辑,不考虑其他问题。我们整理包括需求规约在内的各种各样的素材,(使用面向对象方法)提炼出系统需要封装的各种领域知识,并整理成类模型。模型中,根据不同的变化频率,各种概念分别放置在类、属性、关系等合适的地方,没有冗余。

(可以理解成程序运行在一个存储空间无限大,通信和运算速度无限快且分布在全宇宙的计算机上,不用考虑什么硬盘、内存、Cache、加载,万事俱备,只欠你所分析的领域知识。)

为了方便后面的叙述,我把这称为问题①——这是最难也最想逃避的。

我依然用之前的订单例子,图9左下角的类模型,只涉及目标系统需要封装的领域概念和逻辑,不涉及任何具体实现,我们把它叫作分析模型。

(图9左下角的类模型没有行为部分。如果需要充分表达行为逻辑,可以(使用面向对象方法)建立类的状态机模型,把类的各种行为逻辑分别放在状态机的触发器、迁移动作,状态内动作等合适的地方,同样,没有冗余。)

图片

图9 问题①②③

问题②

现实中的计算机和网络,运行空间、运行速度、通信速度都是有限制的。

如果人们对性能的要求没那么敏感,当前的计算资源又允许,那么实现模型(代码、存储……)和图9左下角的分析模型之间映射非常直接。

假设出现这样的情况:需要频繁地获得订单的总价,而频繁计算订单总价导致出现了不可调和的性能问题,解决方案之一是在实现模型中给“订单”添加一个冗余属性(用数据库语言就是冗余字段)“总价”,这就是《DDD诊所》的情况。图9右上角就是一个C#实现的“订单”类,“总价”前面有个“/”,说明这个属性是派生的(derived)。

为了方便后面的叙述,我把这称为问题②。

*可能有读者会想:这样的话,《DDD诊所》那个“订单总价=sum(订单行总价)”(前文已说过,其实应该是“总价==sum(…订单行.总价.…)”)的不变式不就有了吗?依然没有,领域逻辑并没有变化,分析模型不需要改动。这种添加冗余以及同步的套路不属于领域逻辑,实际上和具体的领域订单、物流、制造、社交……是正交的——这也是常见的批量刷废话套路,把两个正交的东西混在一起。

问题③

假设考虑到开发团队的结构,把系统分成多个“微服务”,分由各个小团队应用各自的技术栈独立完成。这时,可能会在多个“微服务”中有“订单”类。这应该就是林宁说的“拓展系统和团队”。

为了方便后面的叙述,我把这称为问题③,见图9右下角。

*注意:本文列出的问题①②③是对应《DDD诊所》的内容以及林宁的评论内容,不代表只存在这么几个问题。

(2)问题②③的出现对问题①有帮助吗?

没有帮助。

如果问题②③的出现有助于解决问题①,我们早就可以这样干了,不用等到真的有问题②③出现,假想它们存在就行了。

建模人员正在抓耳挠腮地思考和整理各种领域逻辑(问题①),太难了!

有办法。给原问题叠加一个性能问题“10亿并发用户”(问题②),再叠加一个团队问题“100个团队并行开发”(问题③),果然,问题①迎刃而解。

可能会有人提到类似下面这个:

Scott Millett等的“Patterns, Principles, and Practices of Domain-Driven Design”书中举了一个例子,一个庞大的Product类怎样被分解成若干个上下文中的小Product类:

图片

图10 “Patterns, Principles, and Practices of Domain-Driven Design”中的例子

张逸所著的《解构领域驱动设计》中,也用了类似的例子:

图片

图11 《解构领域驱动设计》中的例子

这看起来不就是问题③有助于问题①的解决吗?

其实这是问题①没做好。稍微有建模能力的人,谁会把这么一大堆不同变化频率的知识混到一起?

我写过一篇《发现在写代码过程中对需求的认识更清晰了》谈过类似问题,供参考。

图片

图12 《发现在写代码过程中对需求的认识更清晰了》截图

(3)提防伪创新混水摸鱼

李三正在抓耳挠腮地思考问题①,太难了!

这时候,“创新专家”张四过来推销一种“新方法学”,大意是:环境和时代变了,你需要问题②③。

于是,李三抛开问题①不谈,先搞问题②③,工作量一下就饱满了,看着李三忙忙碌碌,李三的老板也开心。

时间过去,也许问题①没解决,但没关系,问题②③那边没有功劳也有苦劳。

时间过去,也许在忙问题②③的同时,如图12所说,懵懵懂懂,问题①也解决了一些。那就刚好证明张四的“新方法学”有助于解决问题①。

总之,问题②③的引入提供了一层遮羞布,掩盖了李三在问题①上的能力不足问题。

于是,李三觉得张四的新方法学“十分受用”。

(4)可以理解和不能忍

我拓展一下《软件方法》第1章的大楼类比:

两座大楼耸立在那里,要判断地震来了哪座大楼不容易塌,要考虑的是大楼的结构、所用的材料、所在位置的地质环境等,和这座楼是哪家公司建造的,要了多少钱,建造大楼的公司内部是怎样的组织结构,甚至大楼是猫建造的、狗建造的、外星人建造的,没有直接关系。

但要研究这些让楼不容易塌的直接影响因素,涉及到艰深的工程力学、流体力学、岩土力学等知识。架构师李三没把这些知识学扎实,正在那里犯愁呢。

这时,张四出现了。张四说,时代变了,现在盖楼要讲“新建筑学”,要考虑到人际关系,要搞好团结。

于是,李三想着反正“老的”工程力学那些我也搞不懂,还是搞“人”轻松一些。这样吧,有几个包工队跟自己混,就分几个包,大家开干就是。

转换思想后,李三每天累并快乐着,灯红酒绿,推杯换盏。

而且,运气好的时候,盖出来大楼确实也能住人。

**********

如果李三说,公司又不是我的,想那么多干什么,这可以理解;

如果李三说,这么干盖楼快,反正老板要的就是在某某大日子到来之前有个样子货交差,这也可以理解;

如果李三说,这么干有利于建筑团队的安定团结(虽然坑顾客),这也可以理解;

但如果李三和张四说,“新建筑学”盖出来的大楼更抗震,甚至到清华大学建筑学院开课“划时代革命性的工程力学”,取代原有的“工程力学”——这就不能忍了!

(5)最后说一个文风问题

短短一句话,就有“让网变成树”、“切成服务”、“拓展系统和团队”三个可以作为“创新大会”演讲标题的高大上用语。

图片

图片

图片

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值