2w字长文助你上手软件领域驱动设计 DDD

目录

1.背景

2.DDD 概要与实践感悟

2.1 复杂性

2.2 领域驱动

2.3 怎么才算 DDD/p>

3.问题空间&解空间

3.1 问题空间&解空间

3.2 示例-学生管理系统的问题空间

4.领域驱动设计统一过程(DDDRUP)

5.全局分析阶段

5.1 形成统一语言

5.2 价值需求分析

5.3 业务需求分析

5.3.1 业务流程、业务场景、业务服务和业务规则

5.3.2 子领域

6.架构映射阶段

6.1 限界上下文的定义和特征

6.1.1 限界上下文的定义

6.1.2 限界上下文的特征

6.2 限界上下文的识别

6.2.1 按业务维度识别

6.2.2 验证

6.3 上下文映射

6.3.1 防腐层

6.3.2 开放主机服务

6.3.3 发布语言

6.3.4 共享内核

6.3.5 合作者

6.3.6 客户方/供应方

6.3.7 分离方式

6.3.8 遵奉者

6.3.9 大泥球

6.4 示例-SMS 的限界上下文及其映射

7.领域建模阶段

7.1 模型驱动设计

7.1.1 领域模型驱动设计

7.2 领域分析建模

7.2.1 名词建模

7.2.2 动词建模

7.2.3 提取隐式概念

7.2.4 归纳抽象

7.2.5 确认关系

7.2.6 示例-SMS 的领域分析模型

7.3 领域设计建模

7.3.1 设计要素

7.3.2 设计聚合

7.3.3 设计服务

7.3.4 示例-SMS 的领域设计模型

7.4 领域实现建模

7.4.1 领域模型与测试金字塔

7.4.2 测试驱动开发

8.分层架构与代码骨架

8.1 分层架构

8.2 代码骨架

8.2.1 用户接口层

8.2.2 应用层

8.2.3 领域层

8.2.4 基础设施实现层

9.杂谈

9.1 DDD 与微服务

9.2 事务

9.2.1 本地事务

9.2.2 Saga 事务

10. 参考


作者:faryrong,腾讯 CSIG 后台开发工程师

最近看了一本书《解构-领域驱动设计》,书中提出了领域驱动设计统一过程(DDDRUP),它指明了实践 DDD 的具体步骤,并很好地串联了各种概念、模式和思想。因此,我对书本内容做了梳理、简化,融入自己的理解,并结合之前阅读的书籍以及实践经验,最终形成这篇文章。希望可以帮助大伙理顺 DDD 的各种概念、模式和思想,降低上手 DDD 的门槛。

1.背景

领域驱动设计(DDD)由 Eric Evans 提出,并一经《领域驱动设计:软件核心复杂性应对之道》的发布,在软件行业中引起了不少的轰动。DDD 提供的一种新颖的,甚至有点“另类”的思维方式,它在告诉软件开发者“我们要用业务方案来解决业务问题,而不是技术方案解决业务问题”,有点魔法打败魔法的意思。DDD 虽然让人眼前一亮,但是所提倡的理念有点“违背直觉”(对开发人员而言),因此,在当时并没有流行开来。

后来,微服务架构的兴起,大伙惊奇地发现 DDD 是作为划分“微服务边界”的一把利器,并且 DDD 提及的很多设计理念与微服务架构十分契合,因此 DDD 逐渐被开发者们接受并流行起来。毫不夸张地说,了解和学习 DDD 可以算得上是如今软件行业从业者的一门必修课了。

但是!DDD 的学习曲线较为陡峭。作为一个小白,翻阅过很多相关的书籍、KM 文章和分享,但始终觉得未得要领、一知半解。原因有二:a) DDD 涉及的概念繁多,且不同概念的抽象层次不一样,如果我们直白地去理解,往往会感到疑惑,比如:子域和限界上下文都是用于将问题进行归类和收敛,他们的区别是什么)缺少过程指导,难以将概念有序的串联起来。作为方法论,DDD 给出了设计思想,核心原则以及常用工具,但是却缺少细致有序的方法步骤,导致难以上手实践。

幸运的是,最近看了一本书《解构-领域驱动设计》。这本书提出了领域驱动设计统一过程(DDDRUP),它指明了实践 DDD 的具体步骤,并很好地串联了各种概念、模式和思想。因此,我对书本内容做了梳理、简化,融入自己的理解,并结合之前阅读的书籍以及实践经验,最终形成这篇文章。希望可以帮助大伙理顺 DDD 的各种概念、模式和思想,降低上手 DDD 的门槛。

2.DDD 概要与实践感悟

经典必读书籍《领域驱动设计:软件核心复杂性应对之道》的书名包含了两个关键词:领域驱动复杂性,分别代表了 DDD 的核心原则以及解决的问题。

2.1 复杂性

系统的复杂性往往并不在技术上,而是来自领域本身、用户的活动或业务服务。当这种领域复杂性在设计中没有得到解决时,基础技术的构思再好也是无济于事。而系统的复杂度体现在三个方面:规模结构变化

规模:指的是系统所支持的功能点,以及功能点与功能点之间的的关系。DDD 通过子领域,限界上下文,聚合等模式对问题进行拆分和归类,不断收窄问题域,保证聚合边界内所解决的问题集合足够收敛和可控。

结构:指的是系统架构。系统架构是否分层;若分层,每层划分的职责边界是否清晰;架构的基本管理单元是什么,它决定了架构演进时的复杂度。DDD 通过分层架构,独立出领域层,且架构中的每层都有清晰的职责。整体架构的基本管理单元是聚合,它是一个完整的、自治的管理单元,当需要进行服务拆分时,可以直接以聚合作为基本单元进行拆分。

变化:指的是系统响应需求变化的能力。快速响应变化的有效手段是分离不易变逻辑和易变逻辑,”以不变应万变”。而通过分层架构独立的领域层正是不易变的逻辑。领域层是对领域知识的封装,其提供的领域服务具有经验性和前瞻性,是对领域内稳定的领域规则的表达。而领域层以外的应用层和基础设施层则是易变逻辑的封装。保证核心的独立和稳定,通过在调整应用层和基础设施层来实现快速响应需求变化。

2.2 领域驱动

领域驱动指的是以领域作为解决问题切入点,面对业务需求,先提炼出领域概念,并构建领域模型来表达业务问题,而构建过程中我们应该尽可能避免牵扯技术方案或技术细节。而编码实现更像是对领域模型的代码翻译,代码(变量名、方法名、类名等)中要求能够表达领域概念,让人见码明义。

结合实践经验,以下是本人对“领域驱动”的一些见解:

思维模式转变

实践 DDD 以前,我最常使用的是数据驱动设计。它的核心思路针对业务需求进行数据建模:根据业务需求提炼出类,然后通过 ORM 把类映射为表结构,并根据读写性能要求使用范式优化表与表之间的关联关系。数据驱动是从技术的维度解决业务问题,得出的数据模型是对业务需求的直接翻译,并没有蕴含稳定的领域知识/规则。一旦需求发生变化,数据模型就得发生变化,对应的库表的设计也需要进行调整。这种设计思维导致变化从需求穿透到了数据层,中间并没有稳定的,不易变的层级进行阻隔,最终导致系统响应变化的能力很差。

协同方式转变

过去由产品同学提出业务需求,研发同学根据业务需求的 tapd 进行技术方案设计,并编程实现。

这种协同方式的弊端在于:无法形成能够消除认知差异的模型。产品同学从业务角度提出用户需求,这些需求可能是易变的、定制化的,而研发同学在缺少行业经验的情况下,往往会选择直译,即根据需求直接转换为数据模型。而研发同学从技术实现角度设计技术方案,其中涉及很多的技术细节,产品同学无法从中判断是否与自己提出的业务诉求和产品规划相一致,最终形成认知差异。且认知差异会随着迭代不断被放大,最后系统变成一个大泥球。

2w字长文助你上手软件领域驱动设计 DDD

DDD 通过解锁新角色”领域专家”以及模型驱动设计,有效地降低产品和研发的认知差异。领域专家是具有丰富行业经验和领域知识储备的人,他们能够在易变的、定制化的需求中提炼出清晰的边界,稳定的、可复用的领域概念和业务规则,并携手产品和研发共同构建出领域模型。领域模型是对业务需求的知识表达形式,它不涉及具体的技术细节(但能够指导研发同学进行编程实现),因此消除了产品和研发在需求认知上的鸿沟。而模型驱动设计则要求领域模型能够关联业务需求和编码实现,模型的变更意味着需求变更和代码变更,协作围绕模型为中心。

2w字长文助你上手软件领域驱动设计 DDD

精炼循环

精炼循环指的是在统一语言,提炼领域概念,明确边界,构建模型,绑定实现过程中,这些环节相互影响和反馈,在不断的迭代试错-调整以最终沉淀出稳定的、深层次的模型的过程。比如,我们在提炼领域概念的时候会觉得统一语言定义不合理/有歧义,此时我们就会调整统一语言的定义,并重新进行提炼领域概念。通过精炼循环,我们逐步形成稳定的领域模型。在 DDD 中,让领域专家来主导概念提炼、边界划分等宏观设计,原因就在于领域专家的经验和行业洞见来源于过去已经迭代的无数个精炼循环,因此由这些宏观设计推导出来的领域模型,往往都是非常稳定的。

精炼循环的核心是循环,它避免知识只朝单一方向流动,最终因各环节上的认知差异,最终导致模型无法在产品、领域专家和研发中达成一致、模型与实现割裂。

2.3 怎么才算 DDD/h2>

我早期实践 DDD 的时候,认为代码分层遵循四层架构就是 DDD,抑或分离接口和实现,实现下沉至基础设施层就是 DDD,实则不然。结合上述内容,目前个人认为只要满足以下条件即为实践 DDD:

  • 构建出产品、领域专家和研发同学认知一致且便于交流的模型,并且模型与实现紧密绑定;

  • 模型逐步演进,反复消化和精炼;

  • 模型蕴含领域知识,足够稳定。

3.问题空间&解空间

3.1 问题空间&解空间

问题空间和解空间并非 DDD 特有的概念,而是人们为了区分真实世界和理念世界而提出的概念。问题空间表示的是真实世界,是具体的问题、用户的诉求,而解空间则是针对问题空间求解后构建的理念世界,其中包括了解决方案、模型等。

DDD 提出的战略设计覆盖了问题空间和解空间,而战术设计则聚焦在解空间上。明确 DDD 中的概念是作用于问题空间还是解空间,更有助于我们理解它们。

3.2 示例-学生管理系统的问题空间

学生管理系统(Student Management System,下文简称 SMS)作为 DDDRUP 的讲解示例,以下为其问题空间的描述。

4.领域驱动设计统一过程(DDDRUP)

虽然领域驱动设计划分了战略设计战术设计,也提供了诸多模式和工具,但却没有一个统一过程去规范这两个阶段需要执行的活动、交付的工件以及阶段里程碑,甚至没有清晰定义这两个阶段如何衔接、它们之间执行的工作流到底是怎么样的。

而《解构-领域驱动设计》提出的 DDDRUP 给出了更细致的步骤、步骤与步骤之间的衔接,以及明确的阶段里程碑,最重要的是 DDDRUP 可以串联 DDD 的所有概念和模式,非常便于初学者做知识梳理和上手实践。下文我会依照 DDDRUP 的步骤流程进行讲述,而非战略设计+战术设计的思路。(DDDRUP 各步骤与战略&战术设计的关系见下表)。

2w字长文助你上手软件领域驱动设计 DDD

5.全局分析阶段

全局分析阶段对问题空间进行的梳理和分析,形成统一语言(ubiquitous language), 获取问题空间的价值需求以及业务需求

5.1 形成统一语言

统一语言:蕴含领域知识的、团队内统一的领域术语。产品、领域专家以及开发人员掌握的领域知识存在差异,往往导致对同一个事物使用不同的术语。比如,商品的价格(Price)和商品的金额(Amount),它们本质是同一个东西,但是却有不同的术语表示。

统一语言会参与 DDDRUP 的全流程,且会在精炼循环过程中不断进行调整,以反映出更合适、更深层次的领域知识。

根据业务需求形成统一语言,有助于团队对事物的认知达成一致。统一语言可以通过词汇表的形式展示,其中词汇表最好还要包含术语对应的英文描述,便于研发同学在代码层面表达统一语言。示例-SMS 的统一语言词汇表如下。

2w字长文助你上手软件领域驱动设计 DDD

5.2 价值需求分析

价值需求分析主要做的三个工作是:

  1. 识别利益相关者。利益相关者指的是与目标系统存在利益关系的人、团队或组织, 可以简单理解为目标系统的用户,或与目标系统有直接交互的人、团队或组织。

  2. 明确系统愿景。阐明目标系统要做什么,以及为何要做。

  3. 确定系统范围。确定系统问题空间的边界,明确系统什么该做,什么不该做。结合目标系统当前状态未来状态进行判断。当前状态指的是系统的可用资源,包括业务资源、人力资源,资金资源等;而未来的状态则由业务目标、组织的战略规划和产品规划共同构成。

并非任何系统都 DDD,DDD 的核心是解决领域复杂性,若系统逻辑简单,功能不多,引入 DDD 则会得不偿失。而在进行价值需求分析后,我们便能判断是否需要通过 DDD 驱动系统的设计。

5.3 业务需求分析

5.3.1 业务流程、业务场景、业务服务和业务规则

使用业务流程、业务场景、业务服务业务规则来表示业务需求。

业务流程:表示的是一个完整的、端对端的服务过程。

业务场景:按阶段性的业务目标划分业务流程,就可以获得业务场景。在示例-SMS 中,老师修改成绩就分为了老师“提交申请单”,以及教务员“同意申请单”两个场景。

业务服务:角色主动向目标系统发起服务请求完成一次完整的功能交互,以实现业务目标。角色可以用户、策略(定时任务)或者其他系统,完整则强调的是业务服务的执行序列的所有步骤都应该是连续且不可中断的。业务服务是业务需求分析最核心,也是最基础的单元,而业务流程和业务场景是为了更好地分析出业务服务。在示例-SMS 中的“同意申请单”场景中包含了两个业务服务:教务员“同意申请单”和系统“邮件通知”教务员。

业务规则:指对业务服务约束的描述,用于控制业务服务的对外行为。业务规则是业务服务正确性的基础。常见的业务规则有:a) 意如“若… , 就….” 的需求描述,比如示例-SMS 中可提炼出“若成绩录入时间间隔超过一周,不予修改”;b) 具有事务性的操作。

2w字长文助你上手软件领域驱动设计 DDD

5.3.2 子领域

通过业务流程、业务场景和业务服务的梳理,基本可以分析出业务需求所需要的业务服务。然而,业务服务粒度太细,而问题空间又太大,我们需要找一个更粗粒度的业务单元,来帮助我们对业务服务进行聚类,一方面可以降低管理过多细粒度业务服务导致的额外复杂度,另一方面可以帮助领域专家和开发团队分析问题和设计方案时不至于陷入到业务细节中。而这个更粗粒度的业务单元就是子领域

子领域的作用

  • 划分问题空间,作为业务服务分类的边界;

  • 用于分辨问题空间的核心问题和次要问题。

子领域的分类:

  • 核心子领域:能够体现系统愿景,具有产品差异化和核心竞争力的业务服务;

  • 通用子领域:包含的内容缺乏领域个性,具有较强的通用性,例如权限管理和邮件管理;

  • 支撑子领域:包含的内容多为“定制开发”,其为核心子领域的功能提供了支撑。

子领域的功能分类策略:问题空间应该分为哪些子领域,需要团队对目标系统整体进行探索,并根据功能分类策略进行分解。

  • 业务职能:当目标系统运用于企业的生产和管理时,与目标系统业务有关的职能部门往往会影响目标系统的子领域划分,并形成一种简单的映射关系。这是康威定律的一种运用。

  • 业务产品:当目标系统为客户提供诸多具有业务价值的产品时,可以按照产品的内容与方向进行子领域划分。

  • 业务环节对贯穿目标系统的核心业务流程进行阶段划分,然后按照划分出来的每个环节确定子领域。(这也是我们最常用的策略)

  • 业务概念:捕捉目标系统中一目了然的业务概念,将其作为子领域。

划分子领域的过程存在很多经验因素,一个对该行业领域知识了如指掌的领域专家,可以在完成价值需求分析后,结合自身的领域经验,能够选择合适的聚类策略并给出稳定的子领域列表。但,没有领域经验也没有关系!因为根据知识消化循环思路,再经历多个迭代后收敛出来的子领域划分也会逐渐合理,逼急领域专家凭经验得出的子领域划分,只是可能需要的时间要长一些。

6.架构映射阶段

在架构映射阶段,我们需要识别限界上下文,并通过上下文映射表示限界上下文之间的协作关系。

6.1 限界上下文的定义和特征

6.1.1 限界上下文的定义

限界上下文是语义和语境的边界。在问题空间,统一语言形成了团队对领域概念的统一表达,子领域形成了领域概念之间的边界。而在解空间,限界上下文可以看做是统一语言+子领域的融合体,统一语言需要在限界上下文内才具有明确的业务含义。

以电商购物场景为例。在进行商品下单后,系统会生成一个订单;在用户付款完成后,系统也会生成一个订单;到了物流派送流程,系统还会生成一个订单。虽然这三个步骤中的领域概念都叫订单,但是他们的关注点/职责却不同:商品订单关注的是商品详情,支付订单关注的是支付金额和分润情况,物流订单关注的是收货地址。也就是说,商品、支付和物流分别为三个限界上下文,而订单作为统一语言需要在特定的限界上下文内,我们才能够明确其关注点/负责的职责。

6.1.2 限界上下文的特征

最小完备:限界上下文在履行属于自己的业务能力时,拥有的领域知识是完整的,无须针对自己的信息去求助别的限界上下文。

自我履行:限界上下文能够根据自己拥有的知识来完成业务能力。自我履行体现了限界上下文纵向切分业务能力的特征。

这里需要强调一下业务模块(横向切分)限界上下文(纵向切分)的区别。业务模块不具备完整、独立的业务能力,它没有按照同一个业务变化的方向进行。而限界上下文是对目标系统架构的纵向切分,切分的依据是从业务进行考虑的领域维度。为了提供完整的业务能力,在根据领域维度进行划分时,还需要考虑支撑业务能力的基础设施实现,如与该业务相关的数据访问逻辑,以及将领域知识持久化的数据库模型,形成纵向的逻辑边界,即限界上下文边界。

2w字长文助你上手软件领域驱动设计 DDD

稳定空间:限界上下文必须防止和减少外部变化带来的影响。

独立进化:指减少限界上下文内部变化对外界产生的影响。

上述的四个特征可以帮助我们验证识别出来的限界上下文。限界上下文划分是否合理、职责分配是否合理(最小完备 & 自我履行),是否合理运用上下文映射的手段隔离外部变化的影响(稳定空间)、是否有合理的封装,对外提供的接口是否稳定(独立进化)/p>

6.2 限界上下文的识别

6.2.1 按业务维度识别

1. 归类

按照业务相关性对业务服务进行归类,业务相关性体现为:

  • 语义相关性:存在相同或相似的领域概念,对应于业务服务描述的名词,如果不同的业务服务操作了相同或相似的对象,即可认为它们存在语义相关性。

  • 功能相关性:体现领域行为的相关性,业务服务是否服务于同一个业务目标。

2. 归纳

归纳是对归类后的限界上下文进行命名。给限界上下文命名的过程,实际上也是对归类是否合理的再一次复查。限界上下文的命名同样需要遵循单一职责原则,它只能代表唯一的最能体现其特征的领域概念。倘若归类不合理,命名就会变得困难,这时候我们就需要反思(遵循知识消化循环)归类是否合理,并重新设计归类。

3. 边界梳理

归类和归纳之后,限界上下文的边界基本已经确定,边界梳理则是根据限界上下文特征(最小完备、自我履行、稳定空间和独立进化)以及子领域进行微调(当然也不排除大调)。

6.2.2 验证

正交原则

正交性:如果两个或更多事物中的一个发生变化,不会影响其他事物,这些事物就是正交的。要破坏变化的传递性,就要保证每个限界上下文对外提供的业务服务不能出现雷同。

奥卡姆剃刀原理

“如无必要,勿增实体”。这是避免过度设计的良方,同样也是我们识别限界上下文的原则。如果对识别出来的限界上下文的准确性依然心存疑虑,比较务实的做法是保证限界上下文具备一定的粗粒度。遵循该原则,意味着当我们没有寻找到必须切分限界上下文的必要证据时,就不要增加新的限界上下文。

6.3 上下文映射

限界上下文封装了分离的业务能力,上下文映射则建立了限界上下文之间的关系。上下文映射提供了各种模式(防腐层、开放主机服务、发布语言、共享内核、合作者、客户方/供应方、分离方式、遵奉者、大泥球),本质是在控制变化在限界上下文之间传递所产生的影响

下文将提供服务的限界上下文称为“上游”上下文(U 表示),消费服务的限界上下文称为“下游”上下文(D 表示)。

6.3.1 防腐层

引入防腐层的目的是为了隔离耦合。防腐层往往位于下游,通过它隔离上游上下文发生的变化。

2w字长文助你上手软件领域驱动设计 DDD

6.3.2 开放主机服务

开放主机服务定义公开服务的协议(亦称为“服务契约”),包括通信方式、传递消息的格式(协议),让限界上下文可以被当做一组服务访问。开放主机服务也可以视为一种承诺,保证开放的服务不会轻易做出变化。

对于进程内的开放主机服务,称为本地服务(对应 DDD 中的应用服务)。

对于进程间的开放主机服务,成为远程服务。根据选择的分布式通信技术的不同,又可以定义出类型不同的远程服务:

  • 面向服务行为,比如基于 RPC,称为提供者(Provider);

  • 面向服务资源,比如基于 REST,称为资源(Resource);

  • 面向事件,比如基于消息中间件,称为订阅者(Subscriber);

  • 面向视图模型,比如基于 MVC,称为控制器(Controller);

2w字长文助你上手软件领域驱动设计 DDD

6.3.3 发布语言

发布语言是一种公共语言,用于两个限界上下文之间的模型转换。防腐层和开放主机服务都是访问领域模型时建立的一层包装,前者针对发起调用的下游(通过基础设施层体现),后者针对响应请求的上游(通过应用层+远程服务),以避免上下游之间的通信集成将各自的领域模型引入进来,造成彼此之间的强耦合。因此,防腐层和开放主机服务操作的对象都不应该是各自的领域模型,这正是引入发布语言的原因。(对于熟悉云 API 的小伙伴就会发现,其实云 API 根据我们定义的接口生成对应的 Request 对象和 Response 对象,并集成在云 API 的 SDK 中,这些对象就是发布语言)。

一般情况下,发布语言根据开放主机服务的服务契约进行定义。

说到这里,我们惊讶地发现防腐层开放主机服务发布语言可以完美联动!

2w字长文助你上手软件领域驱动设计 DDD

6.3.4 共享内核

共享内核指将限界上下文中的领域模型直接暴露给其他限界上下文使用。注意,这会削弱了限界上下文边界的控制力。上面我们讲述的防腐层、开放主机服务以及发布语言无不传达一种思想,限界上下文不能直接暴露自己的领域模型或直接访问其他限界上下文的领域模型,一定要有隔离层!

但是,在特定的场景下,共享内核不见得不是一种合理的方式。任何软件设计决策都要考量成本与收益,只有收益高于成本,决策才是合理的。一般对于一些领域通用的值对象是相对稳定的,这些类型通常属于通用子领域,会被系统中几乎所有的限界上下文复用,那么这些领域模型就适合使用共享内核的方式。共享内核的收益不言而喻,而面临的风险则是共享的领域模型可能产生的变化。

2w字长文助你上手软件领域驱动设计 DDD

6.3.5 合作者

合作关系指的是协作的限界上下文由不同的团队负责,且这些团队之间具有要么一起成功,要么一起失败的强耦合关系。合作者模式要求参与的团队一起做计划、一起提交代码、一起开发和部署,采用持续集成的方式保证两个限界上下文的集成度与一致性,避免因为其中一个团队的修改影响集成点的失败。

6.3.6 客户方/供应方

当一个限界上下文单向地为另一个限界上下文提供服务时,它们对应的团队就形成了客户方/供应方模式。这是最为常见的团队协作模式,客户方作为下游团队,供应方作为上游团队,二者协作的主要内容包括:

  • 下游团队对上游团队提出的服务

  • 上游团队提供的服务采用什么样的协议与调用方式

  • 下游团队针对上游服务的测试策略

  • 上游团队给下游团队承诺的交付日期

  • 当上游服务的协议或调用方式发生变更时,如何控制变更

6.3.7 分离方式

分离方式的团队协作模式是指两个限界上下文之间没有一丁点关系。如果此时双方使用到了相似/相同的领域模型,则可以通过拷贝的方式解决,保证限界上下文之间的物理隔离!

6.3.8 遵奉者

当上游的限界上下文处于强势地位,且上游团队响应不积极时,我们可以采用遵奉者模式。即下游严格遵从上游团队的模型,以消除复杂的转换逻辑。

当下游团队选择“遵奉”于上游团队设计的模型时,意味着:

  • 可以直接复用上游上下文的模型(好的);

  • 减少了两个限界上下文之间模型的转换成本(好的);

  • 使得下游限界上下文对上游产生了模型上的强依赖(坏的)。

6.3.9 大泥球

一定要避免制造大泥球!大泥球的特点:

  • 越来越多的聚合因为不合理的关联和依赖导致交叉污染;

  • 对大泥球的维护牵一发而动全身;

  • 强调“个人英雄主义”,只有个别“超人”能够理清逻辑。

6.4 示例-SMS 的限界上下文及其映射

示例-SMS 的限界上下文可划分为:

  • 成绩上下文

  • 课程上下文

  • 审批上下文

  • 权限上下文

  • 邮件上下文

上下文映射图如下所示。

2w字长文助你上手软件领域驱动设计 DDD

7.领域建模阶段

领域建模阶段由领域分析建模,领域设计建模和领域实现建模组成。在正式讲解建模活动前,先了解一下什么是模型驱动设计。

7.1 模型驱动设计

模型是一种知识形式,它对知识进行了选择性的简化和有意的结构化,从而解决信息超载的问题。模型便于人们理解信息的意义,并专注核心问题。

建模过程一般由分析活动设计活动实现活动组成。每一次建模活动都是一次对知识的提炼和转换,并产生相应的模型,即分析模型设计模型实现模型

建模过程并非是分析、设计和实现单向的前后串行过程,而是相互影响,不断切换和递进的关系。模型驱动设计的建模过程是:分析中蕴含了设计,设计中夹带了实现,甚至实现后还要回溯到设计和分析的一种迭代的螺旋上升的演进过程。

根据分解问题的视角不同,我们日常建立的模型可以大致分为以下三类:

  • 数据模型:将问题空间抽取出来的概念视为数据信息,在求解过程中关注数据实体的样式和它们之间的关系,由此建立的模型就是数据模型。

  • 服务模型:将每个问题视为目标系统为客户端提供的服务,在求解过程就会关注客户端发起的请求以及服务返回的响应,由此建立的模型就是服务模型。

  • 领域模型:围绕问题空间的业务需求,在求解过程中力求提炼出表达领域知识的逻辑概念,由此建立的模型就是领域模型。

7.1.1 领域模型驱动设计

一个优秀的领域模型应该具备以下的特征(我们也可以说具备这些特征的模型就是领域模型):

  • 运用统一语言来表达领域中的概念;

  • 蕴含业务活动和规则等领域知识;

  • 对领域知识进行适度的提炼和抽象;

  • 由一个迭代的演进过程建立;

  • 有助于产品、领域专家和开发同学进行交流。

领域建模阶段目的便是建立领域模型。领域模型由领域分析模型领域设计模型以及领域实现模型共同组成,它们也分别是领域

来源:文晓武

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年5月17日
下一篇 2022年5月17日

相关推荐