领域驱动设计四论

DDD 四论

在系统开发这一行摸爬滚打多年,开发过众多业务系统,也接触过各种技术流派,结合众多书籍,从技术风格上分为两派:

  • 江湖派:重实践,重个人领悟,以思想体系见长
  • 学院派:重规范,重系统训练,以方法体系见长
  • 在技术 区里,热闹程度可谓冰火两重天,江湖一派热热闹闹,追随者众,耳濡目染,无师自通;学院一派则是养在深闺人未识,门可罗雀,亟待挖掘。

    有趣的是,两派似乎从不往来,从两派的著作来看,江湖派偶尔涉足学院派的领域,但学院派决口不提江湖派的东西。看来在技术圈子里,同样也是文人相轻、道不同不相为谋,各自混各自的圈子。

  • 结构论:战术建模与战略建模
  • *过程论:系统重构与软件工程
  • 语言论:基于模型的统一语言
  • 建模论:模型驱动的领域建模
  • 这个划分,其实体现了本人长期的实践体悟和理论思考,并不是随意为之。王阳明晚年将其毕生所学,浓缩为四句话:

  • 无善无恶心之体,
  • 有善有恶意之动,
  • 知善知恶是良知,
  • 为善去恶是格物。
  • 本人将 DDD 概括为四论,不是要盖棺定论,只是希望去掉枝叶保留主干,避免 DDD 逐渐沦为一种坊间流传的野狐禅。

    绕不开的复杂度

    复杂度

    《没有银弹:软件工程的本质性与附属性工作》(No Silver Bullet—Essence and Accidents of Software Engineering),1986 年发表一篇关于软件工程的经典论文,打开了复杂度这个潘多拉魔盒。论文中 brooks 把失控的、复杂的软件项目比作中世纪的狼人,只有银弹才能杀死它。但是由于软件开发的本质复杂性,使得真正的银弹并不存在,即没有任何技术或管理上的进展, 能够独立地许诺十年内使软件系统项目生产率、 可靠性或简洁性获得数量级上的进步。

    到现在几十年过去了,狼人依旧未被杀死,人月依旧是个神话,银弹在哪?

    复杂度也许永远不能消除,但我们可以分析复杂度,进而管理复杂度。在软件开发领域,大体上可以将复杂度分为三类:软件本身固有的复杂度,业务逻辑带来的复杂度,以及技术复杂度。

    应对之道

    从我看过的几本经典书籍来看,软件核心复杂性的应对之道,不外乎就是:抓住总体,理清局部

    DDD 与复杂度

    DDD 提供了一些应对复杂度的具体方法:

  • 通过架构设计来分离业务复杂度和技术复杂度
  • 通过限界上下文将一个大系统切分为若干高内聚低耦合的子领域
  • 通过领域模型对业务领域的知识进行抽象
  • 什么是领域

    Domain-Driven Design 还是 Model-Driven Design ?

    在《领域驱动设计·软件核心复杂性应对之道》这本书里,对什么是“领域”,只有简单的一句话:“每个软件程序是为了执行用户的某项活动,或是满足用户的某种需求。这些用户应用软件的问题区域就是软件的领域。” 除此之外,讲的更多的是“模型” 或者“领域模型”,纵观全书,“Model-Driven Design” 是其核心概念之一。某种程度上,把“领域驱动设计”改为“模型驱动设计”,一点问题都没有,甚至“模型驱动设计”更能体现整本书的精髓。

    领域划分与领域模型

    总的来说,“领域”这个词可能承载了太多含义。领域既可以表示整个业务系统,也可以表示其中的某个核心域或者支撑子域。在本书中,我将尽可能地区分这些概念。当谈及到业务系统中的某个方面时,我会使用诸如“核心域”或者“子域”以示区别。

    由于“领域模型”包含了“领域”这个词,我们可能会认为应该为整个业务系统创建一个单一的、内聚的、全功能式的模型。然而,这并不是我们使用 DDD 的目标。正好相反,在 DDD 中,一个领域被分为若干子域,领域模型在限界上下文中完成开发。事实上,在开发一个领域模型时,我们关注的通常只是这个业务系统的某个方面。试图创建一个全功能的领域模型是非常困难的,并且很容易导致失败。

    关于 DDD 的一个野狐禅

    本人接触领域驱动大概已有 10 年时间,直到近期才听同事说起过“贫血模型”,然后在 上一查,原来是 martin fowler 在 2003 年发的一个 blog:AnemicDomainModel。其本意只是指出,很多人其实误用了领域模型,把领域模型的 Model 直接等同于 MVC 的 Model,导致 model 里只有数据没有逻辑,fowler 给这种情形取了一个名字叫“Anemic Domain Model”。

    Fowler 把这种误用概括为一种反模式 AnemicDomainModel,在流传过程中,有人把“Anemic Domain Model”翻译为“贫血模型”,后面又衍生出“充血模型”“失血模型”“胀血模型”,这些就都是穿凿附会之说,跟 Martin Fowler 无关,跟领域驱动更无关系。很多 blog 都是把 DDD 跟 MVC/DAO/ORM/TDD 放在一个层次来理解,这说明完全不理解 DDD,实际上 DDD 是跟 MBSE/SysML/UML 是一个层次的概念。

    在领域驱动的经典著作里,完全没有 AnemicDomainModel 的相关提法,把 martin fowler 的说法穿凿附会乱加引申,将领域模型简单分类为“失血,贫血,充血,胀血”,这个大概就属于野狐禅了,完全背离了 DDD 的本质精神。

    结构论

    战略建模与战术建模的划分

    DDD 相关经典著作,一般都将 DDD 划分为战略设计和战术设计两部分。《领域驱动设计·软件核心复杂性应对之道》和《实现领域驱动设计》都明确有战略设计的提法,其他两本书则明确提出了战略设计和战术设计。

    顾名思义,战略设计就是宏观设计,即对系统整体进行建模,称之为“战略建模”;战术设计则是微观设计,即对系统的局部进行细粒度的建模,称之为“战术建模”。

    战略建模

    战略设计原则必须指导设计决策,以便减少各个部分之间的互相依赖,在使设计意图更为清晰的同时而又不失去关键的互操作性和协同性。战略设计原则必须把模型的重点放在捕获系统的概念核心,也就是系统的“远景”上。而且在完成这些目标的同时又不能为项目带来麻烦。为了帮助实现这些目标,我们提出了战略设计的 3 大原则:上下文、精炼和大型结构

    理解大型系统的常用方法:

  • 使用隐喻,比如电动汽车其实就是一台电脑装了四个轮子
  • 把大型系统从逻辑上切分成若干层,分而治之
  • 把大型系统提炼为一个抽象结构,例如,冯诺依曼计算机=IO+CPU+Memory
  • DDD 中的上下文(Context)是个让人迷惑的词,从一种比较宽泛的视角来看的话,Context 可以对应于 UML 的 class 或者 SysML 的 block,即 Context 可理解为是一个类或模块。ContextMap 则对应 UML/SysML 的 Relationship。

    战略精炼:对核心域进一步萃取,过滤掉不必要的杂质,使得其方向更清晰,内容更准确、内核更精干。

    战略精炼:相关方法可以分为三大类,即提炼出内核、进一步精炼内核、增强沟通。

    战术建模

    战术建模侧重于从微观层面对系统进行建模。DDD 提到的战术建模方法主要是构造块与柔性设计。

    构造块:在类、对象、组合、继承等层次上对系统进行设计。按照 DDD 的术语,我们可以把服务、事件、实体、值对象归类为原子块,把资源库、聚合、工厂归类为组合块。

    柔性设计:列举了一些设计原则,类似于常见的软件设计原则,只是换了一种说法。例如通过明确概念、避免概念过载、处理副作用等做法,得到的是一个高内聚低耦合的设计。

    软件设计原则,说到底不外乎,模块内高内聚,模块间去耦合。下面是学院派总结的一些原则,可以对照来看。

    过程论

    DDD 关于开发过程的论述

  • 通过重构加深理解
  • 敏捷开发过程与持续交付
  • 引入软件工程的规范方法
  • 通过重构加深理解

    领域驱动设计的重点在系统设计阶段,但领域驱动设计同样将重构作为重要内容。《领域驱动设计·软件核心复杂性应对之道》这本书第三部分共 6 章的篇幅在介绍重构。看其内容,名为重构,实则仍然是设计,例如概念建模、柔性设计、分析模式、设计模式等等,基本都是一些偏宏观的设计内容。内容上,跟 Martin Fowler 的《重构:改善既有代码的设计》还是有很大区别。

    论重构,当首推软件开发一代宗师 Martin Fowler 的《重构:改善既有代码的设计》,无人能出其右。

    《重构:改善既有代码的设计》,这本书的主要内容,用一个脑图展示如下。

    从这本书开始,”代码坏味道”成为开发领域的一个标准术语。在重构的过程中,有一些基于经验的原则可供参考。

    就重构的具体做法上,可以从函数、数据、业务逻辑三个方面来展开。

    某种程度而言,软件工程师仍然是手工业者,软件开发仍然没有银弹,重构仍然是软件在生长过程中不可或缺的调校手段。

    因此,我们也不用迷信什么银弹,也不必忌讳什么过度设计与设计不足,通过多次重构迭代,让正确的设计逐步显现。

    敏捷开发与持续交付

    在《领域驱动设计》出版的年代(2004),正值敏捷开发和极限编程大行其道的年代,《领域驱动设计》尽管不局限于某种固定的开发过程,但主要还是面向“敏捷开发过程”这一新体系。

    二十年后的今天,敏捷开发和极限编程早已式微,但乔梁老师的著作《持续交付》给我们提供了新的视角。乔梁老师在其著作《持续交付》里梳理了软件工程的进化史。最近两三年,作为腾讯外聘高级管理顾问,其“价值探索-快速验证”的持续交付 2.0 双环模型,令人印象深刻。

    引入软件工程的规范方法

    领域驱动设计统一过程(DDDUP)参考了统一过程(rationalunified process,RUP)的二维开发模型。整个过程的二维模型图所示,横轴代表推动领域驱动设计在构建过程中的时间,体现了过程的动态结构,构成元素主要为 3 个阶段(phase),每个阶段可以由多个迭代构成;纵轴表现了领域驱动设计在各个阶段中执行的活动,体现了过程的静态结构,构成元素包括工作流(workflow)和元模型(meta model)。

    语言论

    统一语言

    UBIQUITOUS LANGUAGE,有的书翻译为通用语言,有的书翻译为统一语言,其核心要点为:

  • 一个团队一种语言,语言统一才能沟通
  • 将领域模型作为统一语言的核心,基于模型进行沟通
  • 业务领域的例子

    业务领域有业务领域的语言,不同业务有不同语言。下面是腾讯动漫的一个例子。

    注:图片摘自公司同事的 km 文章

    技术领域的例子

    以下是几个技术领域的例子,不同层次有不同层次的语言。

    在《领域驱动设计·软件核心复杂性应对之道》这本书里,明确提到了设计模式,这可以看做比编程语言更高一个层级的语言,提高了思维的抽象层次。

    《微服务架构设计模式》和《面向模式的软件架构》在架构设计层面建立起了一套完整的模式语言,比设计模式再高一个层级。

    建模论

    江湖派 vs 学院派

    尽管在各自的著作中,两派是互不感冒,但在平时的工作中,通常不分派别,哪派管用用哪派。在诊断方法上,中医只能讲出望闻问切,西医能讲的东西就太多太多。在系统建模这个事情上,学院派能讲的东西,无论深度广度还是精度,都远超江湖派。

    Model 是结合点

    DDD、UML 和 Sysml 的底层逻辑全都指向 Model,Model 是三种技术

    声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

    上一篇 2022年10月12日
    下一篇 2022年10月12日

    相关推荐