如果没有认识到一项技术的局限性,就不能说彻底理解了它。科学家们清醒的知道,一个理论有它适用的范围和条件——即使经典如物理学,在微观场景牛顿力学也开始失效。

在软件工程领域,一项技术或者编程思想的出现总是伴随着它的使用场景和问题背景出现的,无脑吹或滥用一项技术不仅给我们带来麻烦,也会让这项技术早早的被引导到奇怪的地方。

DDD 正处于被无脑吹捧和贬损的风口浪尖。

时不时看到一些《微服务已死 DDD 才是王道》之类的文章,令人啼笑皆非。

其实 DDD 没有那么神通广大,相比算法、大数据、操作系统等计算机科学基础来说,它的内容并不多,也不难。

更加直白一点,精通 DDD 除了可以顶上一个高大上的“架构师”头衔外,实际上没有想象中那么多用处。

关于如何通俗的理解 DDD,可以参考我的一篇文章《领域驱动设计(DDD)基础》open in new window,在那篇文章中我使用了餐厅作为例子通俗的说明了 DDD 的一些概念。

在我做架构和建模咨询、培训的过程中(这类咨询难在理解业务本质,构建合适的模型,而不是 DDD 本身),发现太多的公司对 DDD 给予不切实际的厚望,以及教条般的应用《领域驱动设计》《实现领域驱动设计》 这些书籍中的做法。

最终导致 DDD 让相当一部分团队“不知道如何着手写代码了”,甚至当我建议适当取舍一些实践时,有人拿出《领域驱动设计》书籍放到 xx 页,告诉我 Eric 是这样实现的。

我仿佛回到了中世纪的教皇时代,看到一群人拿着《神经》辩论,如果我继续坚持我的看法,是不是会被烧死?

在有些时候引入 DDD 带来的麻烦比收益更多,我们就不得不去思考:

  1. DDD 的局限性是什么?
  2. 秉承什么样的原则使用 DDD?
  3. 应该如何应用 DDD?

01. DDD 的局限性?

DDD 最大的局限性是:它不是一个像数据库、操作系统这样特定的技术,而是一种开发思想或者说理念。

这就让它变成一门玄学,下限极低,上限极高。

每个人都可以谈谈对它的理解,又难以判定对错。换句话说,就是不具备可证伪性,只能通过写出的代码可维护性有限的判断 DDD 的实施是否带来的好处。

《领域驱动设计》这本书主要包括了围绕模型驱动和业务(领域)驱动思想的一系列实践,它的内容并不多。作为对面向对象的拓展,将它限定在软件开发思想的范畴内其实并没有问题。

麻烦在思想被当做具体的技术进行落地(我在社区活动 《DDD 龙门阵》中提到 DDD 不需要落地也是这个原因),人们不得不找到更多的内容来弥补在具体实践上的不足。

虽然,我经历的 DDD 咨询项目,企业的本意是对原有系统进行分布式化(微服务/中台)的改造,但是不知怎么就变成了 DDD 项目。

DDD 的另外一个局限性是 “被过度演绎”。人们将各种实践都纳入了 DDD 的范畴,让 DDD 概念的外延急剧的膨胀,变得臃肿不堪。概念外延的扩展导致内涵缩小,于是让 DDD 变得什么都不是。

大量内容并不在 Eric 的《领域驱动设计》一书中。

比如:

  • DDD 和微服务的关系(DDD 一书的出版在 2003 年,微服务晚了很多,但是 DDD 一书中有分布式系统相关的内容)。
  • 六边形架构。
  • 整洁架构。
  • Event Souring。

其次,DDD 中具有很多过于理想化的实践,这些实践带来额外的成本。而成熟的架构师,应该学会如何权衡。

比如:

  • 充血模型和贫血模型。过度充血带来的麻烦比贫血模型更多,贫血模型带来问题仅仅是业务一致性难以保证,代码封装性比较弱,但是也可以用其他途径解决。但是,过度充血(比如订单能自己结账)甚至会让代码无法编写下去,出现操作者操作自身的行为。
  • 聚合的整存整取。使用聚合作为持久化的单位,而不是业务一致性的单位,让持久化变得困难,性能降低。
  • 4 层架构。在有些场景中,框架完成了接入层、技术设施层,如果依然在业务代码中坚持 4 层架构,会让层数过多,开发成本急剧上升。

这些问题在工作中亲身经历过,在我以前的文章中都单独讨论过。

再其次,现在流行的方式是通过事件风暴来实现模型的建立,但是事件风暴是建立在头脑风暴上的,它的可操作性较低,严重依赖主持人的认知水平,优点则是可以通过“吵架”让咨询师挖掘领域专家和技术专家脑子中真正的业务知识。

最后,虽然 DDD 存在局限性,但是依然具有大量的优秀实践,这些才是我们应该重点关注的:

  • 显示的定义概念来统一语言。
  • 聚合可以用于定义业务一致性的基本单位。
  • 限界上下文可以用来定义相对独立能力的基本单位。
  • 4 层结构中,区分了应用层很领域层,可以更清晰的定义出组件的职责。

这些实践对于我们现在为日益复杂的分布式系统建立领域模型非常有用,建立出更适合复杂业务的架构。正因为如此,使用 DDD 的成本是如此之高,非常不适用于快速做原型验证的项目,也不适合技术复杂性远远大于业务复杂性的项目。

我们可以考虑不要一开始就是用太多 DDD 实践,而是在团队认知负担过高时再来做模型梳理(当然,雇个好的咨询师可以少去很多麻烦)。

02. 秉承什么原则应用 DDD?

当我们了解了 DDD 的局限性后,我们应该秉承什么原则应用 DDD呢?这里说的原则并不是 DDD 书中说的几个软件开发原则,而是如何看待和使用 DDD 的原则。

前面说过,DDD 是一种软件开发思想,而不是具体的某个技术。这和哲学非常像,历史上哲学家们也为大量的形而上学问题费劲脑筋,尤其是经院哲学几乎都是空洞无物的猜测和思辨,无论对错上帝也不会真的张口回应。

为了做出对各种理论的评价,哲学家马克思·韦伯理性两种模式:价值理性和工具理性。

通俗的来说,价值理性是指以实现某种价值观为目的,来评价和衡量事物,并进行决策。而工具理性是指关注采纳的理论能否作为工具解决具体的问题。

举个例子来说,有一些开发者具有“代码强迫症”,希望通过充血模型实现技术复杂度和业务复杂度的彻底分离,让代码变得 “干净”,这就是一种价值理性。而对于工具理性者来说,DDD 的聚合可以解决业务一致性的问题,避免破坏性的改动伤害业务逻辑,那么是否绝对 “干净”则不是追求的目标,这样就可以选择性的取用 DDD 的实践即可。

价值理性和工具理性还有一个显著的区别,价值理性往往具有大量的主观判断,人类又是极其复杂动物,很难在价值观上取得共识。

03. 应该如何应用 DDD?

虽然 DDD 的内容既不多,也不难,但是要把 DDD 的各种概念弄明白,并且让它在项目中起到作用并不是一件容易的事情。

应用 DDD 和学习 DDD 差不多,很多人没有意识到的是: DDD 的很多实践,我们早已经在面向对象的各种文章和实践中大量使用了。

Eric 总结了这些实践然后给予了一些正式的名字,让隐性知识显性化。

如果我们打开 Hibernate 的源码,我们会看到 Domain、Entity、Repository 等一系列相似的概念。

在平时的开发工作中,我们也会不自觉地给一组紧密相关的实体一个领航员,然后分配它一个 Service 来管理这组实体,只不过在 DDD 之前,我们没有叫它聚合。

我们会把相对独立的一大块代码和实体分包,在 DDD 之前,我们没有叫它上下文。

就像牛顿和莱布尼茨发明微积分之前,已经有许多的数学家在使用极限的思想来解决问题。

所以应用 DDD 最重要的原则是:

少量取用,大量练习。

我们应该追求实用主义,不必将 DDD 的全部纳入项目中,一个项目也不必全是 DDD。

就像工具理性那样,我们把 Eric 的《领域驱动设计》当做一个工具箱,当项目出现问题时候,可以有的放矢的挑选一些合适的实践来解决问题。

DDD 应该是手段,而非目的。

在禅宗中有个故事说的是经书就像手指,而佛法是月亮,只看书就像以手指月只见手指不见月亮。

DDD 应该是手指,软件设计才是月亮。

对于企业来说,软件设计也是手指,实现商业价值才是月亮。

这也是架构师在企业中推动 DDD 时和业务人员所冲突的地方。

04. 总结

总之来说,在软件工程中,“没有银弹” 已经成为了共识,DDD 也不是银弹。

关注软件设计,让合适的组件完成合适的事情,那么不使用 DDD 看起来也没有任何问题,不是吗?

需要警惕的是,当我们获得一个思维模型的时候(DDD 就是这样一个思维模型),又会被这个思维模型困住。

Last Updated:
Contributors: lin