一、前言

最近 10 年的互联网发展,从电子商务到移动互联,再到“互联网+”与传统行业的互联网转型,是一个非常痛苦的转型过程。在这个过程中,一方面会给我们带来诸多的挑战,另一方面又会给我们带来无尽的机会,它会带来更多的新兴市场、新兴产业与全新业务,给我们带来全新的发展机遇。然而,在面对全新业务、全新增长点的时候,我们能不能把握住这样的机遇呢?

随着业务的不断迭代和发展,用户数量也在高速增长。市场和业务环境的多变,需求往往是复杂多变的,如何快速响应变更。需求变更一次,版本迭代一次,软件就修改一次,软件修改一次,质量就下降一次。不论第一次的设计质量有多高,软件经历不了几次变更,就进入一种低质量、难以维护的状态。需要设计一套易于扩展和维护的系统。

二、概述

2.1 什么是DDD

DDD,全称Domain-Driven Design, 是一种处理复杂领域的设计思想,它试图分离技术实现的复杂性,并围绕业务概念构建领域模型来控制业务的复杂性,以解决软件难以理解,难以演进的问题。它通过边界划分将复杂业务领域简单化,帮我们设计出清晰的领域和应用边界,可以很容易地实现架构演进。

DDD将项目的主要焦点放在核心领域和领域逻辑上。基于一个模型进行复杂的设计,在技术和领域专家之间发起创造性的协作,迭代地切割问题的概念性核心。

DDD是面向对象的设计思想,是面向对象设计的一种升华。

2.2 背景

2003 年埃里克·埃文斯(Eric Evans)发表了《领域驱动设计》(Domain-Driven Design –Tackling Complexity in the Heart of Software)这本书,从此领域驱动设计(Domain Driven Design,简称 DDD)诞生。DDD 核心思想是通过领域驱动设计方法定义领域模型,从而确定业务和应用边界,保证业务模型与代码模型的一致性。

但 DDD 提出后在软件开发领域一直都是“雷声大,雨点小”!直到 Martin Fowler 提出微服务架构,DDD 才真正迎来了自己的时代。
大部分的Java程序员开发生涯是从学习J2EE经典的分层理论开始的(Action、Service、Dao),在这种分层理论中,我们基本没有啥机会使用那些所谓的“行为型”的设计模式,这里的核心原因,就是J2EE经典分层的开发方式是“贫血模型”。

2.3 使用DDD的价值

  • 获得了一个非常有用的领域模型
  • 业务得到了更准确的定义和理解
  • 领域专家可以为软件设计做出贡献
  • 更好的用户体验
  • 清晰的模型边界
  • 更好的企业架构

领域专家、设计人员、开发人员通过领域模型进行交流,彼此共享知识和信息,有利于沟通概念的统一,减少交流的障碍,防止需求变样。

设计足够精良且符合业务需求的领域模型能够快速的响应需求变化。

软件的本质就是对真实世界的模拟。因此,我们会有一种想法,能不能将软件设计与真实世界对应起来,真实世界是什么样子,那么软件世界就怎么设计。如果是这样的话,那么在每次需求变更时,将变更还原到真实世界中,看看真实世界是什么样子的,根据真实世界进行变更。这样,日后不论怎么变更,经过多少轮变更,都按照这样的方法进行设计,就不会迷失方向,设计质量就可以得到保证,这就是“领域驱动设计”的思想。

2.4 实施DDD所面临的挑战

  • 为创建通用语言腾出时间和精力
  • 持续地将领域专家引入项目
  • 改变开发者对领域的思考方式

三、如何DDD

3.1 战略设计与战术设计

DDD的实施分战略设计与战术设计。

战略建模:战略设计主要从业务视角出发,建立业务领域模型,划分领域边界,建立通用语言的限界上下文,限界上下文可以作为微服务设计的参考边界。战略建模是以一种最宏观的角度去审视整个项目对它进行拆分,来划分“界限上下文”,最终形成一个具有俯瞰视角的“上下文映射图”。

战术建模:战术设计则从技术视角出发,侧重于领域模型的技术实现,完成软件开发和落地,包括:聚合根、实体、值对象、领域服务、应用服务和资源库等代码逻辑的设计和实现。

在实施过程中,我们需要明确“通用语言”,通用语言和限界上下文(Bounded Context)同时构成了DDD的两大支柱,并且它们是相辅相成的。

通用语言是以一种领域专家、设计人员、开发人员都能理解的语言作为相互交流的工具,在不断交流的过程中发现和挖出一些主要的领域概念,然后将这些概念设计成一个领域模型。由领域模型驱动软件设计,用代码来表现该领域模型。领域需求的最初细节,在功能层面通过领域专家的讨论得出。

领域模型贯穿软件分析、设计,以及开发的整个过程;领域专家、设计人员、开发人员通过领域模型进行交流,彼此共享知识与信息;因为大家面向的都是同一个模型,所以可以防止需求走样,可以让软件设计开发人员做出来的软件真正满足需求。

在这个过程中,需要业务专家的参与,他们才是最懂业务的人,他们能用他们已有的对这个领域的知识,与我们一起设计开发系统,让我们的系统更符合实际需要。典型的例子,在设计一个系统时,如果两个对象或模型有关联关系,那么这个关联关系是一对多、一对一还是多对多,有时我们并不能很好的判断出来,这个时候就需要领域专家应用他们的知识来帮助我们一起处理这部分的设计。

领域专家并不是一个职位,他可以是精通业务的任何人。可能是软件产品的设计者,甚至可能是销售员。
在设计的过程中,我们书面设计的文档不可能面面俱到,应尽量将文档减至最少,并且主要用它来补充代码和口头交流,解释我们模型中的概念,避免文档与项目脱节,设计的重要细节应该在代码中体现出来。

3.2 DDD领域模型

DDD的研究方式与自然科学的研究方法类似,下面以研究一棵树的过程举例
谈一谈 DDD-LMLPHP

  • 第一步:确定研究对象,即研究领域,这里是一棵桃树。
  • 第二步:对研究对象进行细分,将桃树细分为器官,器官又分为营养器官和生殖器官两种。其中营养器官包括根、茎和叶,生殖器官包括花、果实和种子。桃树的知识体系是我们已经确定要研究的问题域,对应 DDD 的领域。根、茎、叶、花、果实和种子等器官则是细分后的问题子域。这个过程就是 DDD 将领域细分为多个子域的过程。
  • 第三步:对器官进行细分,将器官细分为组织。比如,叶子器官可细分为保护组织、营养组织和输导组织等。这个过程就是 DDD 将子域进一步细分为多个子域的过程。
  • 第四步:对组织进行细分,将组织细分为细胞,细胞成为我们研究的最小单元。细胞之间的细胞壁确定了单元的边界,也确定了研究的最小边界。

在领域不断划分的过程中,领域会细分为不同的子域,子域可以根据自身重要性和功能属性划分为三类子域,它们分别是:核心域、通用域和支撑域。决定产品和公司核心竞争力的子域是核心域,它是业务成功的主要因素和公司的核心竞争力。没有太多个性化的诉求,同时被多个子域使用的通用功能子域是通用域。还有一种功能子域是必需的,但既不包含决定产品和公司核心竞争力的功能,也不包含通用功能的子域,它就是支撑域。核心域、支撑域和通用域的主要目标是:通过领域划分,区分不同子域在公司内的不同功能属性和重要性,从而公司可对不同子域采取不同的资源投入和建设策略,其关注度也会不一样。

3.3 限界上下文

限界上下文用来确定语义所在的领域边界,限界就是领域的边界,而上下文则是语义环境。通过领域的限界上下文,我们就可以在统一的领域边界内用同意的语言进行交流。

比如电商领域的商品一样,商品在不同的阶段有不同术语,在销售阶段是商品,而在运输阶段则变成了货物。同样的一个东西,由于业务领域的不同,赋予了这些术语不同的涵义和职责边界,这个边界就可能会成为未来微服务设计的边界,所以领域边界就是通过限界上下文来定义的。

所以,在微服务中,每个领域模型都有它对于的限界上下文,团队在限界上下文内通用语言交流。领域内所有限界上下文的领域模型构成整个领域的领域模型。

3.4 运用单一职责原则

单一职责原则:软件系统中的每个元素只完成自己职责范围内的事,而将其他的事交给别人去做,我只是去调用。

“一个职责就是软件变化的一个原因”是著名的软件大师 Bob 大叔在他的《敏捷软件开发:原则、模式与实践》中的表述。

单一职责原则要求我们在维护软件的过程中需要不断地进行整理,将软件变化同一个原因的代码放在一起,将软件变化不同原因的代码分开放。

四、总结

深刻理解 DDD 的设计思想和内涵,把握好边界和分层这个大原则,结合企业文化和技术特点,灵活运用战术设计方法,选择最适合的技术和方法解决实际问题。

在每次变更的时候,先回到领域模型,基于业务进行领域模型的变更。然后,再基于领域模型的变更,指导程序的变更。这样,不论经历多少次需求变更,始终能够保持设计质量不退化。这样的设计,才能保障系统始终在低成本的状态下,可持续地不断维护下去。

参考资料

  • 《领域驱动设计——软件核心复杂性应对之道》
  • 《实现领域驱动设计》
  • DDD实战课(欧创新)
12-06 00:52