领域驱动设计-概述

本文作为学习笔记,内容来自《极客时间》专栏《手把手教你落地 DDD》,如有侵权请告知,必定及时删除。

2003 年,Eric Evans 写了《领域驱动设计:软件核心复杂性应对之道》一书,正式提出了这种方法。领域驱动设计的英文是 Domain-Driven Design,所以简称 DDD

1 DDD 基本开发过程

DDD 基本开发过程可以分为两大步骤:模型的建立和模型的实现

  1. 模型的建立,又细分为捕获行为需求和领域建模
  • 捕获行为需求。也就是传统软件工程里的“获取需求”。这一步,我们要识别需求里有哪些流程、哪些功能,每个功能由什么人操作,会产生什么结果。DDD 中比较流行的一种方法,叫做“事件风暴”
  • 领域建模。也就是通过建立领域模型,把需求里的主要业务知识描述清楚。DDD 的领域模型,大体上相当于传统软件工程中的分析模型。
  1. 模型的实现,细分为架构设计、数据库设计、编码实现
  • 架构设计。包括进程间和进程内的架构。比如说微服务设计、中台设计都属于进程间架构。而 DDD 分层架构,通常说的是进程内架构。
  • 然后就可以根据领域模型进行数据库设计,最后是代码实现。

2 建立模型

2.1 捕获行为需求

捕获行为需求的方法有好几种,在传统的软件工程中,最常用的方法是“用例”,也就是 Use Case。但是,Eric Evans 在《领域驱动设计》这本书里,并没有规定捕获行为需求的具体方法。直到 2013 年,一位叫 Alberto 的 DDD 专家提出了“事件风暴”,也就是 Event Storming。


1704598520296.png

2.1.1 识别领域事件

所谓领域事件,就是在业务过程中,业务人员要关注的那些已经发生的事儿。比方说,对于电子商务系统,订单已提交、商品已签收等等,都是领域事件。实际上,领域事件表示的是,业务流程中每个步骤引发的结果。事件风暴的作者认为,从结果入手来梳理需求,比从操作入手,更容易把业务想清楚。事件风暴中的“事件”两个字就来源于领域事件。
另外,咱们还要注意领域事件的命名,如果套用英语的语法来说,一般是完成时 + 被动语态。比如说,订单已提交,这个“已”字就是完成时,代表已经发生的事情。而订单已提交也可以说成订单已“被”提交,实际是被动语态,只不过一般把被字给省掉了。
假设我们要做一个项目管理的系统,可以识别出来的领域事件,如下图所示:

1704599929841.png

2.1.2 识别命令

所谓命令(command),就是引发领域事件的操作,我们可以通过分析领域事件得到。除了识别出命令本身以外,我们通常还要识别出谁执行的命令,以及为了执行命令我们要查询出什么数据。

image.png

  • “合同已签订”是2.1.1中识别出来的领域事件。
  • “签订合同”则代表本小节中表述的命令。
  • “销售人员”代表执行这个命令的主体,代表的是一个角色,而不是具体的个人。
  • “客户”表示在执行该命令时,需要查询出来的数据。

2.1.3 识别领域名词

这里说的领域名词,是从命令、领域事件、执行者、查询数据里找到的名词性概念。例如,对于签订合同这个命令而言,受到影响的名词性概念是“合同”。

image.png

2.1.4 捕获行为需求总结

捕获行为需求三个阶段的作用:

  1. 领域事件:一般会对应一段代码逻辑,这段逻辑可能会最终改变数据库中的数据。另外,在事件驱动的架构中,一个领域事件可能会表现为一个向外部发送的异步消息。
  2. 命令:领域建模时,我们可以通过对命令的走查(walkthrough),细化和验证领域模型。在实现层面,一个命令可能对应前端的一个操作,例如按下按钮;对于后端而言,一个命令可能对应一个 API。
  3. 领域名词:其实识别领域名词的最终目的是要找到领域模型中的对象,一个名词有可能只是一个对象充当的角色,或者对象的属性,还有些名词需要经过合并或拆解后,才是合理的领域对象,而这些需要等到领域建模时才能真正搞清楚。

事件风暴的应用场景:

  1. 事件风暴主要应用在需求不清晰,或者理解不统一的情况下,通过协作的方式理清业务、达成一致,所以通常对于新项目比较适用。
  2. 至于遗留系统改造的情况,如果这个系统的知识已经流失得很严重,那么事件风暴仍然是有意义的。但如果大家对这个系统的业务知识很清楚,只是要进行架构改造,那么事件风暴的意义就不大了。
  3. 如果你的项目里还没有正规的、令人满意的捕获行为需求方法,那就可以使用事件风暴。

2.2 领域建模

领域建模主要有两个目的:

  • 将知识可视化,准确、深刻地反映领域知识,并且在业务和技术人员之间达成一致;
  • 指导系统的设计和编码,也就是说,领域模型应该能够比较容易地转化成数据库模式和代码实现。
    而我们建立领域模型,主要是要识别领域对象(domain object),领域对象之间的关系,以及领域对象的关键属性,必要的时候还要将领域对象组织成模块

2.2.1 识别领域对象与对象间关系

  1. 初步识别实体
    首先,你可以先假定每个领域名词都是一个实体,把它们用类的符号画出来。


    image.png

    其实,在领域建模阶段,我们主要关注的是实体和它们之间的关系。如果实体的名字已经能清晰说明实体的含义,那我们就不需要加属性了。如果名字还不足以充分表达含义,我们可以写几个关键属性,来辅助说明。

  2. 识别“一对一”关联
    租户和企业具有一对一的关系。在有些情况下,一对一的两个实体确实是可以合并的。这取决于这两个概念的关注点是否相同。


    image.png
  3. 识别“一对多”关联
    一个组织里面,可以有多个员工。同时呢,一个组织有且仅有一个上级组织,但是一个组织可以有多个下级组织。可以用下面的图来表示这种关联关系。


    image.png
  4. 增加约束
    凡是约束,必须在程序中的某个地方进行实现,约束也是一种业务规则。
    例如,我想表达,一个开发小组的上级只能是开发中心,而不是一个开发小组下面有多个开发中心,就可以在图中添加约束
    image.png
  5. 识别“多对多”关联
    如果我想表达,一个员工既可以是管理员,又可以是人事人员,同时对于人事人员这个岗位,又可以有多个员工。那么我就可以用下面的图,来表达员工和岗位之间的“多对多”关联。


    image.png

2.2.2 划分模块

如果2.2.1小节中的领域很多,并且领域的关联关系错综复杂,就会导致领域模型难以理解,造成业务与技术人员的认知过载。我们可以把模型中的业务概念组织成若干高内聚的模块(module),而模块之间尽量低耦合。

1704722533051.png

有了模块,我们就可以从两个层面理解模型:宏观层面与微观层面
宏观层面只关心模型中有哪些模块,以及模块间的依赖关系,不关心模块内部的细节。为了达到这个目的,我们可以画出更宏观的包图。像下面这样:
image.png

这里需要区分依赖与关联:

  • 关联表示的是数据上的导航关系。例如,当我们说组织和员工之间具有一对多关联的时候,就意味着,由组织可以找到下面的员工,由员工也可以找到所属的组织。
  • 依赖表示的意思更为广泛。如果 A、B 两个元素,有了 A 才能有 B,那么就可以说 B 依赖于 A。

微观层面,也就是深入到模块内部,了解实体和关联等等的细节。微观层面的输出就是各个模块内部对应的领域模型。
通过这种分而治之的方法,我们可以在一定程度上管理复杂性,解决认知过载的问题。

2.2.3 完善领域模型

  1. 完善业务规则。在领域建模的过程中,我们可以识别出来很多需求移交过程中,没有考虑到的点,可以把这些没有考虑到的点,连同已经识别到的,一起整理成一张业务规则表格。
    image.png
  2. 建立词汇表。主要有两个作用:
  • 通过词汇表来规范领域模型中的词汇。用于统一语言。
  • 可以用于后续的编程命名。


    image.png

3 设计与实现

领域建模与传统的需求分析区别在于:DDD 强调领域模型要兼顾业务和技术两个视角,避免了传统软件工程中分析模型和设计模型相互割裂的风险。可以概括为:

  • 领域模型要和业务需求一致。
  • 系统实现要和领域模型一致。
  • 领域模型中的每个元素,都应该通过某种方式在系统实现中有所体现。
  • 统一沟通业务人员与技术人员的语言。

3.1 数据库设计

经过第2节的建立模型以后,就可以根据模型来设计数据库了,这块内容目前工作中没有涉及,先暂时给自己留个TODO吧,后面用到了再来补充这块笔记。

3.2 分层架构

我们为什么要采用分层架构呢?原因就是为了避免“大泥球”式的代码。“大泥球”式的代码指各种业务逻辑都混杂在一起,通常会出现以下几个问题:

  • 首先,很难单独识别出反映领域逻辑的代码,从而难以保证与领域模型的一致性。
  • 其次,应该内聚的逻辑分散在不同地方,应该解耦的逻辑又混在一起,造成代码难以理解。
  • 再次,修改业务代码,可能会影响技术代码,修改技术代码,又可能会影响业务代码,造成代码很难维护。
  • 最后,经过一段时间的维护,代码变得日益混乱,代码中出现大量重复和不一致,经常出现质量问题。

分层架构就是解决大泥球问题的一种最佳实践,可以有两种等价的画法,一种由内而外,另一种自下而上,如下所示:


image.png
  1. DDD 对代码架构最核心的要求就是要将领域层分离出来。领域层封装了领域数据和逻辑,我们前面的领域模型所对应的代码,主要就体现在领域层。只有将领域层独立出来,才能保证与领域模型的一致,也才能让领域层独立演化。这里叫Domain层。
  2. 领域层封装的逻辑通常是细粒度的,并不适合直接作为 API 暴露给外部。另外,还有一些不属于领域层的横切关注点,比如像事务控制,应该单独处理。所以,我们往往要在领域层外面再加一层,也就是应用层。也就是我们通常写的Service层。应用层本身并不包含领域逻辑,而是对领域层中的逻辑进行封装和编排
  3. 除了业务功能之外,程序里还有另一个重要的关注点——输入输出技术。我们的系统要和外界打交道,可以通过不同技术来实现,比如 Restful API、 RPC,以及传统的 Web 页面等等。我们在应用层外面再加一层,专门处理输入输出技术。也就是我们熟悉的Controller层。
  4. 我们需要一种适配器把具体的持久化技术和Service应用层以及Domain领域层隔离开,而仓库就充当了这种适配器。我们的项目里面,通常把这一层叫做Repository。用于实现访问数据库、下游接口等读、写数据逻辑。
  5. 到现在为止,我们已经讲了 DDD 分层架构中最主要的几层,但还有另外一些代码没有考虑。比如说,我们写了一些用于字符串和日期处理的工具类,这些工具可能被上面说的任何一层调用。事实上,我们可以认为这些代码和前面说的各层根本不在同一个维度,它们是对各层代码起到公共的支撑作用的。也就是Common层。

这里呢,我对原文中的分层架构做了一定的修改,使其更适合自己目前手上的项目。如下图所示:


image.png

如果一个逻辑需要和领域专家讨论才能确认的,就是领域逻辑;如果领域专家根本不感兴趣的,多半就是应用逻辑。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容