再谈领域驱动设计

本文从需求分析到API设计,试图描述领域驱动设计的过程及思想。同时也能看的出领域驱动设计并不是孤立存在的,它为解决开发团队和业务人员之间沟通而生,进而驱动微服务的划分以及API的设计。

作为一个领域驱动设计的实践者,我切实感受到了领域驱动为软件开发带来的好处,同时在实践领域驱动的过程中也感受到了困难,这种困难体现在工程实践的方方面面,例如什么是领域驱动的最佳设计?如何把书本上的设计灵活的应用在自己的项目上?如何跟团队成员就设计达成一致?

本文尝试从领域驱动设计的目的出发,试图通过简单的描述来说明领域驱动设计的思想。

为什么需要领域驱动设计

作为一个软件开发者,多数人以为自己的职责就是编写代码,然而软件开发不是工厂流水线,如果所有的软件开发者不停的开发新功能而不关心设计,那么软件开发过程将会变得越来越复杂,关于这一点大家应该都有不同程度的感受。

软件开发工程师的工作是通过软件来解决问题,编写代码只是其中的一部分工作,设计和交流同样重要。而领域驱动设计就是一个让软件开发工程师交流和共享领域知识的途径。

共享领域知识的重要性

作为一个问题的解决者,能否理解和认识问题的前因后果至关重要。很明显,如果你只看到了问题的表面,或者对事实有曲解,你显然不会找到一个有效的解决方案。对于开发者,如果你编写的代码只是你的理解,而不是领域专家的理解,你如何保证线上产品的质量?

那么如何保证开发者编写的代码就是领域专家的想法?最简单的办法就是让领域专家来编写代码,但是这种方案可遇不可求,还有没有别的办法呢?

如果领域专家,开发团队以及代码能够共享一个模型,这将有效减少不同利益相关者的沟通及交流,并且会确保所有人都在解决同一个问题。

这个想法要求开发者能够把代码设计为一个反映业务的模型,而这正是领域驱动设计的核心思想。

通过业务事件来理解问题域

为了在领域专家和开发者之间建立一个共享模型,收集需求并理解业务是第一步。收集需求和理解业务的方式多种多样,而事件风暴经常被用来达到这一目的。业务逻辑可以看做是一系列状态的转换过程,而这些过程转换又被称为领域事件。比如“订单已提交”就是一个领域事件,如果把这个领域事件看做是订单业务的开始,通过梳理”订单已支付”以及”订单已出库”等后续的领域事件,就可以理解整个订单业务。此时对业务的理解被称为“问题域”。

把问题域划分为问题子域

通过事件风暴,开发团队和领域专家已经对整个”问题域”有了理解,但是现在着手解决“问题域”还有点早。当我们在面对一个大的问题时,自然而然会想到先将大的问题划分成若干个小问题,然后再考虑各个击破。接下来的一步就是把大的问题域划分为若干个小的问题域。我们有一个网上商城的问题域,能不能把它分割为更小的问题域?

答案肯定的,我们把网上商城的问题分为:“订单”,“销售”,“市场”,“财务”,“采购”等若干个小问题域,再针对小的问题域分而治之。小的问题域在领域驱动设计中被称为“问题子域”。

使用限界上下文创建解决方案

理解了问题域并划分为问题子域并不意味着你就能创建出一个好的方案,你无法针对问题子域的所有信息设计出一个解决方案,你的解决方案只会专注于那些有助于解决该问题子域的信息,对于不相关的信息则会人为的屏蔽掉。

为什么叫限界?

在现实世界中,领域的边界很模糊,但是要设计一个好的解决方案,我们需要对问题子域加上一个边界,将不重要的信息排除在边界外。让解决方案专心解决重点问题。

为什么叫上下文?

每个上下文都代表着该解决方案的专业知识。在同一个上下文里,我们共享统一的语言和一致的设计。

通过界限上下文人为将问题子域限制在有限的界限内,你才可以着手创建解决方案。

创建统一语言

团队之间共享的术语和词汇被称为统一语言。统一语言用来定义业务领域的共享模型,当然可以用在项目的任何地方,包括需求分析和设计,最重要的是统一语言还需要出现在代码中。另外,统一语言在不同的界限上下文中往往不能够通用,例如在“认证上下文”中提到“用户”,在“机票订单上下文”中叫做“乘客”。

领域建模

有了界限上下文,让解决方案聚焦在最有用的信息里,你才可以着手建立共享模型。

如何才能建立一个不错的共享模型呢?

使用可视化的图示似乎是一个不错的想法,但实际上画出一个能够表达所有领域知识的图示并不是一个简单的工作;如果你有数据库开发相关的经验,你可能会想到通过表和主外键来表达领域知识,如果你有这样的想法那你就错了,在领域驱动设计中讲究通过领域逻辑来驱动设计和开发工作,而不是通过数据库模型来驱动开发。

在领域驱动设计中这一步叫做”领域建模“,你应该用代码建立一个反映领域知识的模型,这个模型跟领域专家口中的领域知识是一致的。领域模型是提供业务能力的核心部件,也是整个应用程序提供业务能力的核心。

领域建模中的其他概念

对于开发者而言领域建模至关重要,也是最考验开发者功底的一个环节。一方面开发者需要抽象出一个跟领域专家口中一致的模型,另一方面开发者还需要通过代码将这个模型表达出来。你需要恰如其分的使用一些面向对象的技巧把领域知识抽象到一个代码模型中,在这个过程中你需要了解”值对象”,”实体“,”聚合根“等概念,在此不再细说。

领域模型的持久化

在领域建模以及之前的步骤中,我们都没有提及数据库,因为领域驱动设计的核心是用代码建立一个共享模型,而数据库设计根本就不是领域驱动设计关心的内容。

但是终究我们还是要把领域模型的状态持久化到数据库中,有没有办法在不关心数据库表结构的情况下,将已经建立好的领域模型持久化?主流ORM的Code First恰好匹配我们现在的处境,已经有一点为领域驱动设计而生的味道了。

但是即便是ORM的Code First也会对领域模型有侵入,你可能需要根据不同的ORM为模型加上一些注解或者配置之类的代码,这跟领域驱动设计其实是相互违背的,我们希望用代码创建一个纯净的领域模型,这个模型封装着领域专家的领域知识,除此之外的代码都跟领域模型是无关。

领域事件及事件溯源

解决上面问题的思路是引入领域事件和事件溯源。领域模型在提供业务能力的过程,就是领域模型状态发生变化的过程。一旦领域模型的状态发生了变化,就会产生一个事件,这跟事件风暴中提到的业务事件是一致的,例如”用户已下单“。订单模型在提供”用户已下单“的业务能力后发生了状态变化。事件溯源的思路就是只持久化领域事件,然后通过还原事件的方式将领域模型还原在最新的状态。

通过采取事件溯源,就可以将领域模型持久化跟数据库完全解耦。

微服务和领域驱动设计

我们通过领域驱动设计的思路来分析和发现问题域,通过分解把问题域划分为问题子域,通过人为加限制的方式将问题子域转换为限界上下文。而这个过程就是我们分解微服务的过程,一般来说每一个限界上下文都可以映射为一个微服务,但也不是绝对的,具体情况具体分析。

微服务的交互和集成

每一个微服务专注于解决对应的限界上下文中的问题,并不代表微服务之间没有交流。单个微服务的领域模型在提供服务的过程中会产生领域事件,领域事件为基于事件驱动(Event based)的微服务集成提供了基础,如果在微服务之间架设一条消息总线(不同于ESB,ESB被认为是反模式)。不同的微服务将自己产生的领域事件广播在消息总线上,微服务之间通过订阅自己感兴趣的事件就能完成微服务的集成。

HATEOAS和领域模型

迄今为止我们已经建立了领域模型,创建了微服务,通过消息和领域事件完成了微服务的集成。还需要把微服务的能力通过REST API展现出来,微服务在对外提供能力的过程就是领域模型状态发生变化的过程,如果将领域模型理解为一个设计精良的状态机也一点不为过。如果设法将领域模型在某个状态下能够提供的能力通过REST API的的返回结果表达出来,这就是HATEOAS的核心思想。REST API不但可以提供某种能力,还可以告诉消费者此时领域模型能够提供的其他能力。

结束语

本文从需求分析到API设计,试图描述领域驱动设计的过程及思想。同时也能看的出领域驱动设计并不是孤立存在的,它为解决开发团队和业务人员之间沟通而生,进而驱动微服务划分以及API的设计,领域驱动设计并不是遥不可及的方法论,每一个专业术语和思想都是为了解决基本的问题而定义,希望本篇博客能够带你走入领域驱动设计。

文/ThoughtWorks张阳

(0)

相关推荐

  • 远卓:数字化转型的精益化思考

    内容摘要:以数字化驱动的业务转型战略为中心,制定组件化业务能力规划,以此建立敏捷/轻量的业务架构:在业务架构内将业务组件映射为子域,在子域内通过DDD设计应用架构,同时进一步修正业务架构. 建设数字中 ...

  • 走向卓越,领域驱动设计的思维方式

    软件系统是以特定的代码解决现实世界的复杂问题.软件开发的最大困难就是应对复杂度,复杂度可能来源于各个方面.领域驱动设计的概念是 2004 年 Evic Evans 提出的 Domain-Driven ...

  • 使用函数式语言实践DDD

    长期以来我都在实践OOP,进而通过OOP来实现DDD,通过面向对象的技巧来建立一个领域模型.OO的一些特性在建立领域模型时显得恰如其分,能否掌握OO的技巧,对创建领域模型有着至关重要的作用.这篇文章为 ...

  • 一文看懂领域驱动设计!

    本文作者为长沙.NET社区开发者微笑刺客,转载已获得作者授权. 前言 什么是领域,我习惯描述的是制药领域.环境领域.建筑领域.金融领域等,而在领域内,各种业务规则.业务知识盛行,如何有效的把控规则的变 ...

  • 领域驱动设计在马蜂窝优惠中心重构中的实践

    前言 正如领域驱动设计之父 Eric Evans 所著一书的书名所述,领域驱动设计(Domain Driven Design)是一种软件核心复杂性应对之道. 在我们解决现实业务问题时,会面对非常复杂的 ...

  • 谈DDD领域驱动设计和建模

    作者:人月神话,新浪博客同名 简介:多年SOA规划建设,私有云PaaS平台架构设计经验,长期从事一线项目实践 今天谈下领域驱动设计方面的内容,其中部分内容来源于<领域驱动设计:软件核心复杂性应对 ...

  • 如何运用领域驱动设计 - 聚合

    目录 概述 何为聚合 演化案例 发现实体关系 开始划分边界吧 选取一个聚合根 通过聚合根保护你的内部对象 聚合的一些特性 通过ID引用 聚合真的是不变的吗 小的聚合 一致性 总结 概述 在前几篇的博文 ...

  • 如何运用领域驱动设计 - 工作单元

    新年伊始,祝大家喜乐如意,爱和幸福"鼠"不尽!♫. ♪♬.♩♫ 概述 在上一篇 <如何运用领域驱动设计 - 存储库> 的文章中,我们讲述了有关仓储的概念和使用规范.仓储 ...

  • 领域驱动设计适用于军用软件开发吗?

    领域驱动设计这个概念提出已经超过10年. 近些年来,国内已经有一些互联网公司开始试水领域驱动设计.作为军用软件开发来说,领域驱动设计适合吗? 领域驱动设计自其诞生之日起就是为了解决软件的复杂性的.领域 ...

  • 为什么说领域驱动设计可以提升软件的质量水平?

    领域驱动设计是一套方法论,指导我们将复杂问题进行拆分.拆分出各个子系统间的关联以及是如何运转的,帮助我们解决大型的复杂系统在落地中遇到的问题. 在学习领域驱动设计的过程中我越来越感觉到领域驱动设计可以 ...

  • DDD领域驱动设计真就一文不值?

    在互联网快速发展的这几年来,微服务.领域驱动设计等已经非常流行,并成为目前软件开发行业的主流趋势. 大家都知道,微服务划分的一个重要理论基础就是领域驱动设计.但由于 DDD 门槛高.概念多,体系庞大又 ...

  • 深入理解领域驱动设计中的聚合

    聚合模式是 DDD 的模式结构中较为难于理解的一个,也是 DDD 学习曲线中的一个关键障碍.合理地设计聚合,能清晰地表述业务一致性,也更容易带来清晰的实现,设计不合理的聚合,甚至在设计中没有聚合的概念 ...

  • 领域驱动设计(DDD)理论与方法

    DDD由来与优势 软件架构设计的真正目的是解决软件复杂度带来的问题,软件复杂度由来主要由三方面:高并发场景下的对软件高性能要求.业务场景对软件高可用要求.持续变化的业务以及业务扩张和增加需求对软件扩展 ...

  • 领域驱动设计(DDD)在爱奇艺打赏业务的实践

    领域驱动设计(Domain-Driven Design,以下简称DDD)思潮的形成要追述到30几年前,17年前,Eirc Evans定义了领域驱动设计的概念.DDD一直为传统行业的软件工程师提供软件设 ...