业务建模: CQRS是鸡肋?


从2004年DDD诞生以来一直做领域驱动方面的实践,业务建模的过程可以说是一个痛并快乐着的过程,对于CQRS,很早就想写点什么东西,CQRS引入了对象状态、事件溯源(Event Sourcing)、快照(Snapshots)和事件存储(Event Store)等概念,Query Model与Command Model分来设计,相对于经典的DDD的设计实践无意是重大的变革。解决经典DDD复杂数据查询的问题,领域事件飞来飞去,很好很强大,建模的触角直接延伸解决分布式应用、系统性能等方面问题,从纯粹描述领域模型的角度考虑,是否有意或被迫把DDD解决的“问题域”给扩大化了,是否应该把一些架构的东西也吸收近来。简单的说,CQRS对于我们描述领域问题提供了多大助益???

经典的DDD的确实在复杂报表查询上存在很大不足,CQRS很好解决了这个问题,也许这个CQRS提出的初衷:》,但经典DDD实践上是采用其他的替代方案来实现,如使用原生SQL查询,将查询结果以值对象形式返回。CQRS的领域事件的订阅发布,也可以用经典DDD的Service来描述。DDD技术角度讨论实现文章已经很多,领域建模描述领域问(业务领域)的文章去不多。当然了,本人水平有限,CQRS也没具体实践过,理解有可能有偏差,愿“好事”者就这方面进行交流:)
事件保证Command与Query的数据的一致性,是CQRS的基础,事件无疑是个领域对象,但是否应该重领域对象分离出来做一个单独的领域概念,其初衷是否为适应架构设计的需要还是为了描述领域问题的需要?

当初DDD设计初衷的为了“描述业务领域问题,柔性设计”,具有“技术的不相关性”,在这个意义上,CQRS似乎有几分“反DDD经典模式”的味道。

(经典DDD的模型要“升级”到CQRS,必须重构模型,这个重构的原因居然不是来自“业务领域”的变化)

是否有必要讨论一下"架构"与"领域"之间的关系?

“领域”建模是否需要迁就于系统架构?
“架构”的变化如何还保证“领域”模型的稳定?

[该贴被clonalman于2012-08-26 13:27修改过]

写得很有深度,CQRS确实为了让DDD落地,而技术架构和业务领域的实现总是有一段距离,如何弥补这个鸿沟诞生了事件驱动架构EDA或CQRS、还有DCI以及BDD等等。

至于业务领域与技术架构的关系,我个人认为根据业务领域选择合适的技术架构,根据业务领域去优化微调技术架构。

比如如果业务领域是面向普通大众用户的,那么架构性能就很重要,而CQRS也能解决这个问题,因为C命令和Q查询分离实际是写读分离,POST/GET分离,伸缩性很好,见另外一篇文章:破除CQRS的神话

1、理论上,用任何架构或不使用架构都能使DDD落地

2、业务模型不应该有面向大众或小众区别,一个系统100个人可以使用,100万使用崩溃,能说100人使用的领域模型是正确,100万人使用领域模型就变为错误了吗?

领域建模的边界必须是有限,系统性能架构来解决,领域与架构的解决“问题域”边界是否是交叉的?(除非领域建模的“问题域”就是架构本身)

愿意还是不愿意,个人觉得CQRS都不能放在领域模型当中,它能发挥的作用的理想场所应该在基础设施层,而且在“领域层”与“基础设施层”还需要一层的“胶水”,来粘合“领域层”与“基础设施层”的距离,同时消除“基础设施层”由于不同架构选型对“领域层”的冲击。
不是知道Banq是否有这方面的思考?这个“胶水”应该是一个什么东西?
[该贴被clonalman于2012-08-27 12:35修改过]

2012-08-27 12:25 "@clonalman"的内容
个人觉得CQRS都不能放在领域模型当中 ...

这个听起来有点奇怪,个人认为,这不能算是一类的东西,CQRS是一种架构,在CQRS实现里,UI触发Command,Command Executor中调用Domain Model处理逻辑,领域模型里可以触发领域事件,这样看的话,在CQRS架构里,领域模型是其中的一个部分。
从另一方面,DDD是一种建模思想,强调统一语言的使用,没有指定说要落地为什么样的架构。

DDD就是一种思想与架构无涉,但问题CQRS架构已经明显的入侵到"领域层",从而使“领域层”模型稳定性不可保证

如果能把CQRS的元素从领域层剔除,并将之约束在“基础设施层”,那就相对完美了(当然了CQRS也就跟DDD没关系了),
既能保持经典DDD的稳定优美,又能享受CQRS的优点,哪天Eric Evans再搞出什么新概念就可以以不变应万变了。
[该贴被clonalman于2012-08-27 14:27修改过]

2012-08-27 13:00 "@sinaID45452"的内容
在CQRS实现里,UI触发Command,Command Executor中调用Domain Model处理逻辑,领域模型里可以触发领域事件,这样看的话,在CQRS架构里,领域模型是其中的一个部分。 ...

这是DDD领域模型进入CQRS架构后的样子。

业务领域和技术架构的鸿沟我认为是通过领域事件实现的。
写命令:POST --> 业务领域模型 -->领域事件 --> 技术架构。
读查询:GET <-- 技术架构
这样一个整体实现架构我们称为CQRS架构。

我以前也狠狠地思考过,领域事件算不算业务领域的一部分呢?如果不算,那么我们日常也用事件来表达很多行为动作。

通过BDD行为驱动开发受到启发,BDD认为需求是这样表达的,需求人员会这样描述需求:
As a [Role]
I want [Feature]
so that [benefit]

这个描述我们可以使用下面模板转换:
Given some initial context (the givens),
When an event occurs,
then ensure some outcomes.

很明显,这个模板里有场景和事件以及结果状态,所以,虽然有时需求并没有直接出现事件描述,但是在使用BDD方法转换模板时,我们会抓到这个事件。

场景、事件以及状态三元素并不是BDD独有,四色原型彩色UML分析法也包含,DCI也包含,D代表状态,C代表场景,I代表行为事件,当然这些好像没有BDD来得更显式。


1、BDD的需求分析方法一个假设前提:
所有的需求都是三段式(Role/Feature/Benefit)或最终分解为三段式,这个假设成立吗?
2、BDD推倒出来的仅仅是一个用户需求模型,需求模型三段式到设计模型三段式是否是一一对应是个问题?
从哲学认识角度来说:现实模型>需求模型>设计模型,需求、设计的过程都是一个抽象精简的过程。
3、退一万步讲假设需求模型与设计模型如上面描述的一一对应成立,
需求模型上:I want Feature 到 设计模型上:an event occurs已经严重失真,"an event occurs" 能满足 "I want feature" 的,设计上主观排斥了其他途径实现 I want feature 的可能性,换言之,让an event occurs变成了设计上唯一选择,其结果就是系统运行去来“domain event”就满天飞

如果真的是这样的话,可以推倒出的有趣结论,有效实践DDD,必须一切从领域出发,忘记掉所有的技巧、模式(就象张无忌要练成太极的前提是要忘掉以前的所有招式)

设计上一个需求模型推导出的各种设计模型就总结为四色、DCI等模式经验。
理论上一个需求模型可推导出无限的设计模型,所以设计模型模式也是无限的,但在特定的场景下,只有一个种就接近需求模型与现实模型,只用一种设计模型模式要函盖所有需求模型是不出偏差是不可能的。

2012-08-27 21:24 "@clonalman"的内容
让an event occurs变成了设计上唯一选择,其结果就是系统运行去来“domain event”就满天飞 ...

言之有理,an event occurs的事件并不是domain event的事件,两者分别属于不同边界内的事件。

需求模型与设计模型之间的鸿沟很难Map,因为设计模型需要照顾考虑到计算机语言的特点,而现在计算机语言无非是Java等命令式以状态为主和Scala等函数式语言。

从需求模型上:I want Feature 能够推到到 设计模型上:an event occurs也是基于上面现实考虑,I want Feature可以推导出很多结果。

感谢banq的回复,

关于需求模型与设计模型,多年实践经验(理解上可能会有偏差)我觉得需求模型与设计模型都不应该具有计算机语言的相关,只有在设计模型编码实现时,才要考虑具体使用语言,采用什么架构。
1、需求模型输出的可能是:1、BDD(三段式) 2、TDD(测试用例) 3、RUP(用例及其规格) 4、自己写的一些文档(不使用任何模式)
2、设计模型输出可以是ER图、UML或者其他伪代码等,应该也不具有语言相关性,但有设计思想的差异。

原本的讨论“技术架构”与“模领域型”,现在变成“需求”与“设计”了,
问一下Banq,未来DDD发展的方向到底是什么?
是会结合一些架构特性演变出一些DDD变种(象CQRS),还是会回归经典DDD?
[该贴被clonalman于2012-08-28 17:44修改过]

2012-08-28 17:36 "@clonalman"的内容
问一下Banq,未来DDD发展的方向到底是什么?
是会结合一些架构特性演变出一些DDD变种(象CQRS),还是会回归经典DDD? ...

我理解你讲的设计模型,经典分析设计代码是分离的,而DDD提出统一模型试图将三者统一在一起,因为他只是开个头,后面有大量细节工作要探索,有人提出领域事件和CQRS算是一种衔接方案。

我前面提出的观点:场景 事件和状态是一种类似DDD中提出的统一语言,但是不同代码人员看这句话还是有不同理解,习惯Java等图灵状态的程序员认为这类似状态机;而习惯函数语言的程序员则看到函数行为的因素。因此,不管从哪个角度看,他们如果看到设计模型能映射落地到代码就可以了,这也达到Evans提出DDD的统一模型的初衷。

所以,以后DDD发展还是会在融合这个方向继续深入,其实BDD或TDD等敏捷方法已经提出抛弃那种分析设计代码分离瀑布法,而是直接把用户故事场景带入到开发环境,不断迭代,消灭分析设计代码的传统鸿沟。

以上只是我个人看法,抛砖引玉而已。

不管传统方法还是敏捷方法追求分析设计编码的结果都上一致,只是在输出结果过程与结果的内容有差异,像RUP强调分析设计编码按部就班,输出结果(文档或代码)大而全,XP强调交叉、回溯,输出结果精简、隐喻,最小模型。

我觉得,DDD仅做为一个业务领域描述工具,可能会更好