[

Jakub Lambrych

](https://.com/@lambrych?source=post_page—–55e10b7ecc0f——————————–)

战略设计主要概念概述

领域驱动设计(DDD)是一种近年来获得巨大关注的软件设计方法。然而,完全吸收 DDD 需要时间,因为它引入了一些新概念,例如绑定上下文、通用语言等。

本文旨在通过一个例子来解释DDD的战略设计概念。我们将探讨 Elixir 大学的领域,这是一家为其学生实施学生注册系统的机构。
我们将看到 DDD 战略设计框架如何映射到大学领域、设计流程和组织结构。

这些知识将为进一步探索 DDD 战术设计模式和实现细节奠定基础。这些将在我的其他文章中介绍。

 目录

· 战略设计——简洁
· 我们身处太空——问题空间
· 示例域:Elixir 大学
 · 子域名
  ∘ 通用子域
 ∘ 支持子域名
 ∘ 核心领域
· 核心领域 |> 通用语言 |> 有界上下文
∘ 普遍语言的例子
∘ 用有界上下文驯服模型
· 子域与限界上下文
 · 上下文映射
∘ 上下文映射的类型
  ∘ 领域事件
 · 概括

战略设计——简洁

DDD 中的战略设计是一组原则和实践,重点关注:

  1. 识别并定义业务 Core DomainsSubdomains
  2. 在系统的不同部分之间建立清晰的界限( Bounded Contexts )。
  3. 在每个 Bounded Context. 中开发共享语言 ( Ubiquitous Language )
  4. 不同限界上下文和 Teams ( Context Mapping ) 之间的映射关系。
  5. 使软件架构与业务战略和领域结构保持一致。

我发现领域驱动设计的独特之处在于它不仅仅是一组组织我们有关领域的知识的概念。它实际上更进一步:它调查了有助于成功软件设计的人和组织因素。

在领域驱动设计中, Team 在该方法的成功实施中起着至关重要的作用。 DDD 强调 Domain ExpertsSoftware Developers 之间的协作,认识到两者都无法单独完全理解该领域的复杂性。

然而,正是 Organization 决定了各种 Subdomains 的战略重要性,并优先考虑选定的 Core Domain 以进行进一步投资。核心领域是您业务的本质,也是您与其他竞争对手的区别所在。

Team 负责通过创建 Ubiquitous Language 来形成对核心域的共同理解,作为通信和建模的通用语言。团队对该领域的熟悉程度及其集体专业知识对于识别和定义代表核心领域内逻辑边界的 Bounded Context 至关重要。

在“核心域”不同领域工作的各个团队需要通过建立 Context Mappings 进行有效的沟通和协作。上下文映射促进了不同限界上下文之间的集成和交互。

最终,DDD 项目的成功取决于团队致力于不断完善和发展领域模型,确保软件准确反映不断变化的业务需求。

我们身处太空——问题空间

DDD:问题空间和解决方案空间

DDD **Strategic Design** 模式属于 **Problem Space** 。在这个领域,我们专注于对领域及其约束进行高级分析,远离技术细节。这不是编写代码或思考任何特定的技术堆栈或架构。

DDD **Tactical Design** 模式用于逐渐过渡到 **Solution space** 。我们也将越来越接近软件系统的代码。这种转变是由我的另一篇文章中介绍的一系列模型转换驱动的。

**Bounded Context** 是空间之间的主要车辆。现在,您需要知道的是有界上下文只是一个模型。该模型被创建为问题空间的一部分,并进一步开发为适合解决方案空间的软件系统。

示例域:Elixir 大学

每个学年,学生都会浏览大学提供的课程——教学、语言、体育等。课程目录在实际入学开始前几天提供给学生。这使学生有时间准备个人入学计划。
报名正式开始后,每个人都先于其他人跑去报名参加选定的课程。

该大学正在开发一个系统来支持这一点。指定的大学工作人员工作组负责组织学生注册——它是课程注册团队。

 子域

Subdomain 代表较大系统中的特定知识领域和功能。它具有以下特点:

  • 明确的业务目标:将太大而无法使用的遗留域模型分解为具有明确业务目标的逻辑部分,
  • 明确的专业领域:可以指示拥有子域的特定领域专家团队/组织部门,
  • 战略重要性:有助于为业务带来不同的战略价值。

Elixir 大学拥有许多在不同专业领域工作的团队。我们可以区分以下区域/部门(除其他外):薪资子域、课程注册子域、人力资源子域等。

 大学院系

 通用子域

大学不必为每个此类子域开发单独的软件。例如,薪资管理并不是大学业务所特有的。对于任何与员工打交道的机构来说,这是一个相当典型的子域。
这些领域的解决方案通常可以“现成”购买——比内部开发更便宜、质量更高。 DDD 中的此类域称为 Generic Subdomains ,因为它们需要针对其问题的通用解决方案。

 DDD:通用子域

 支持子域名

然而,并不是所有的事情都可以通过通用产品来实现。某些子域可能需要具有不同程度定制的解决方案。当新员工入职 Elixir 大学时,他们会收到一台笔记本电脑、一张桌子和校园门禁卡。
他们还需要通过基本安全课程。管理入职的不同方面可能需要针对大学实施的特定流程量身定制的 ERP 系统。像 ERP 这样的系统非常复杂。
定制通常需要时间和外部顾问来实施。对于需要定制开发但可以外包开发的组织来说具有战略意义的领域称为 Supporting Subdomain

支持子域对于业务具有重要的战略意义,但不考虑我们所说的“核心活动”。
人力资源对于大学来说至关重要,但为了让员工打交道,企业首先需要提供有竞争力的产品,使公司能够雇用并管理员工。

 DDD 支持子域

 核心域

通常,业务或系统只有一个 Core Domain 。该核心领域代表了组织具有战略重要性和竞争优势的主要领域。这是组织应该集中大部分资源和创新努力的地方。

这可能是不存在“现成”产品,或者很难定制的情况。最有可能的是,它是您业务的核心产品,使您与竞争对手区分开来。该域名是您的业务!

 DDD核心域

该大学的核心业务领域之一是教学课程的准备和执行。
从这个角度来看,课程注册是大学特有的功能:它必须处理 Elixir 大学的许多限制,并确保超过 3 万名学生同时访问注册系统。
学生实现个人学习计划的满意度对于大学的声誉至关重要。这是 Elixir 大学区别于竞争对手的因素之一。

核心域 |> 通用语言 |> 有界上下文

领域驱动设计专注于有界上下文内核心领域的通用语言的开发。这是一个由领域专家和软件开发人员团队驱动的迭代过程。

团队开发了一种用于创建领域模型的通用语言

我们先来看看核心概念:

**Ubiquitous Language** :项目中所有利益相关者使用的共享语言,以改善沟通并减少误解。通用语言是:

  • 由团队拥有和维护,
  • 在协作循环中开发
  • 用软件模型表示
  •  依赖于上下文
  • 不是“给予”——它是由团队精心开发的

  **Bounded Context** 是:

  • Ubiquitous Language 的语言和语义边界
  • 围绕核心域特定模型的概念边界

**Team** 软件开发人员和领域专家:

  • 随着时间的推移开发和维护通用语言
  • 过滤掉与战略计划的利益不相符的概念(在有界上下文之外)。

通用语言的例子

在我们的示例中,大学分配了资金并批准实施支持课程注册子域的解决方案。
该大学组建了一支由经验丰富的领域专家和 Elixir 软件开发人员组成的团队来解决这个问题。以下是发生的对话的一瞥:

- 学生在称为“学期”的时间段内参加课程。每学年通常有两个学期。课程注册团队准备了下学期向学生提供的所有课程的目录。学生可以提前浏览此目录。
- 我们如何知道谁可以报名参加课程?
- 嗯,招生部门发送了一份即将成为一年级学生的新入学大学候选人名单。我们还获得了从教务处退学或毕业的学生名单。否则,我们使用前一项的列表。
- 实际报名发生在招生活动期间 - 学期实际开始前几周,以便大学有时间准备教室、课程、场地和其他后勤工作。
- 正确的!关于课程概念——我们绝对应该添加有关课程领导者——导师的信息。
- 可以请您详细介绍一下导师吗?
- 当然!导师是大学雇员,准确地说是教学人员。因此,我们有教学人员,也有只做研究的研究人员。但我们还有其他类型的员工,即维护、安全、管理等。
 - 明白了!
- 我刚刚记得Tutor还从大学获得了一些资产来进行课程:笔记本,打印纸等。

Elixir 大学的领域模型 — 大泥球

对话流畅,沟通良好,团队正在白板上写满概念和它们之间的关系。一开始看起来很棒,因为他们成功地开发了一种流畅且严格的通用语言。
然而,一段时间后,团队意识到模型变得相当大,并且许多概念高度互连。他们开始怀疑所有信息是否确实与他们想要实现的目标相关。
与此同时,他们对删除任何内容都感到不舒服,因为他们认为这可能会损害他们目前付出如此多努力收集的领域知识的准确性。他们似乎被困住了。他们决定去吃午饭。

对于团队来说,目前很难将这些概念合理地划分为功能组并遵循模型单一职责原则。界限并不明显。
到目前为止,团队所取得的成就具有领域设计中所谓的所有特征:“大泥球”。

用有界上下文驯服模型

当边界不清晰时,对问题空间的探索通常会产生一个大的整体模型——“大泥球”。这样的领域巨型模型试图涵盖它的各个方面。发展这样一个庞然大物并不一定是设计师的意图。这实际上是领域分析过程的效果:您从一个概念开始,然后自然地深入了解与其相关的所有其他概念。

为了不偏离正轨,团队应该不断问自己一个问题——“核心是什么?”。到目前为止,课程注册团队在通用语言的开发方面做得相当不错,但他们最终得出的一些概念并不直接对核心领域做出贡献。
当团队回到白板时,他们开始只关注课程注册所需的概念。这是他们的工作结果:

大学核心领域——情境选择

团队决定目前谁是学生的决定超出了课程注册的范围。有关候选人、毕业生等住宿的信息管理属于招生和注册办公室的范围。
他们注意到课程目录创建过程也是独立的,并且由大学的另一个部门驱动。
该目录包括有关课程、导师、场地等的信息。模型的这一部分在某个时候与注册课程团队共享,但不由其管理。有关员工类型的信息被发现完全不相关,因此被删除。
“什么是核心?”的答案帮助团队认识到,招生环境中的关键是学生和课程之间的关系,并由招生活动期间的特定业务规则驱动。

子域与有界上下文

在领域驱动设计中,子域和有界上下文是相关但不同的概念。下表可以帮助您了解差异:

子域与限界上下文

Bounded Context 汇集并隔离服务于独特业务目标的概念。在理想的 DDD 世界中,我们只使用有界上下文以及它们之间的交互(以上下文映射的形式)。
然而,在处理遗留系统时,我们不能保证它们的域模型是在考虑隔离和模块化的情况下构建的。子域用于将遗留域模型分组和划分为组织单元。
目标是在应用纯 DDD 时实现与有界上下文类似的功能结构。大多数时候,单个有界上下文将对应于单个子域。

如果我们仅使用 DDD 方法从头开始开发一个系统,那么我们视图中将只有限界上下文(即 HR 限界上下文、薪资限界上下文等)。 Subdomain 是遗留系统中现有的各种域模型与使用 DDD 开发的新限界上下文之间的概念桥梁。

 上下文映射

在领域驱动设计中, **Context Mapping** 帮助团队了解系统的不同部分如何相互关联和通信。它还澄清了不同域或子域之间的边界和交互。

有界上下文之间的映射建立了团队之间的合作类型

DDD 使团队协作成为一等公民。团队需要相互交互以共享有助于他们实现业务目标的重要信息。在 Elixir 大学,考生加入学院的信息会从招生团队传递到课程招生团队,以便新生可以报名上课。
招生中“候选人”的概念必须转化为课程注册中“学生”的概念。
两个团队合作,使信息共享顺利进行,并达成一些协议(例如,包括候选人在内的招生模式将来不应在没有通知的情况下进行更改,因为这会影响招生操作等)。

 上下文映射的类型

有界上下文之间有几种集成方法。我不会在这里详细介绍它们,以免在这篇介绍性文章中混淆视听。请记住,上下文映射是关于与数据流相关的团队之间的协作类型:

  • 合作伙伴关系:两个团队紧密集成他们的软件系统,将它们视为一个组合系统,并跨团队边界共享模型代码。当团队之间具有信任关系并且其领域模型存在大量重叠时,这种映射样式适用。
  • 共享内核:涉及多个团队共享域模型的子集,该子集被视为共享内核,而每个团队为各自域的其余部分维护自己的上下文和模型。
  • 客户-供应商:一个团队(供应商)开发一个软件系统,由另一个团队(客户)使用,两个团队之间有明确的上下游关系。
  • 顺从者:一个团队(顺从者)坚持另一个团队定义的模型,本质上将另一个团队的模型视为中心的规范模型。
  • 反腐败层:涉及在两个不同模型之间创建转换层,使它们能够进行通信,而无需紧密耦合或需要更改各自的模型。
  • 开放主机服务:一个团队(主机)提供一项可以由其他团队通过插件或适配器扩展的服务,从而实现具有可扩展点的集中式系统。
  • 已发布语言:涉及一个团队(发布者)定义和发布其他团队用于与发布者系统集成的语言或协议,而不共享内部模型。
  • 单独的方式:团队在完全独立的系统上工作,没有集成或共享模型,基本上忽略了其他系统和团队的存在。

 领域事件

上下文映射的实现需要有界上下文进行通信。这是一个相当技术性的问题,但 DDD 战术设计带来了 **Domain Event** 的概念。上下文可以通过异步生成和发送域事件来相互共享有关已发生情况的信息。其他限界上下文可以订阅这些事件并做出相应的反应。

 领域事件

在我们的示例中,准入上下文可以生成 CandidateAdmitted 事件。课程注册上下文可以监听此类事件,并作为响应创建一个新的学生实体,以便新学生可以参加大学下学期的注册活动。

在同一限界上下文中生成和使用 Domain Events 也是可行的。

 概括

在这篇文章中,我们了解到:

  • 由领域专家和软件开发人员组成的团队是设计过程的主要驱动力。
  • 组织优先投资的核心业务功能称为核心域。
  • 一个专门的团队负责通过创建用于通信和建模的通用语言来形成对核心领域的共同理解。
  • 有界上下文的关键作用是通过定义将成为软件解决方案实现来源的模型的边界来管理领域复杂性。
  • 子域:通过将大型系统和遗留域模型分解为具有明确业务目标、专业领域和战略重要性的逻辑部分,帮助概念化大型系统和遗留域模型。
  • 成功的 DDD 实施取决于团队对领域模型的不断完善,以确保软件反映不断变化的业务需求。