65.9K
CodeProject 正在变化。 阅读更多。
Home

领域驱动设计:您需要了解的战略建模。第一部分

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2016年12月1日

CPOL

19分钟阅读

viewsIcon

21367

如果您想依靠战略建模来提高开发效率,那么领域驱动设计就是您所需要的。

在本文中,我想探讨一个许多软件开发人员在处理复杂项目时会遇到的特定问题。也就是说,我们今天的话题是使用战略建模进行领域驱动设计软件开发。

我将要介绍的方法由《领域驱动设计实现》一书的作者 Vaughn Vernon 进行了完美描述。最初,Vaughn 写这本书是为了帮助软件开发人员“乘坐 DDD 飞机”(他小时候经常乘坐小型飞机,因此使用了这个比喻)。据 Vaughn 说,从高处看到的全局图景是每个软件开发人员都需要理解的,关于软件建模和设计的本质,而不会被技术问题所困扰。他确信:当您从高处审视 DDD 的景象时,您将获得战略建模和技术建模的所有关键优势。

要“乘坐”Vaughn 的 DDD 飞机,我们将使用以下战略建模的工具和仪器(模型):UBIQUITOUS LANGUAGE(通用语言)、DOMAIN(领域)、SUBDOMAIN(子域)、CORE DOMAIN(核心域)、BOUNDED CONTEXT(限界上下文)、CONTEXT MAP(上下文映射)。(为了让所有模型更醒目,我将它们大写。)

使用所谓的战术模型进行建模,如 ENTITY(实体)、VALUE OBJECT(值对象)、REPOSITORY(仓储)、EVENT(事件)、AGGREGATE OBJECT(聚合对象),这是 DDD 的简化版。虽然能够使用 DDD 的简化版非常重要,但其模型更偏重技术而非实际,而且;如果您想看到 DDD 的全局景象,您将不得不学习如何在实际实践中实现战略模型。

DDD 实施背后的主要目标是获得特定软件的高质量模型。该模型需要突出并描绘特定的业务目标。为了达到这个目标,开发人员和领域专家都需要共同努力。如果团队习惯于与其他团队和部门合作,那么业务肯定会从中受益。这是因为有效的沟通和人际互动会排除“神秘知识”在团队中流通的可能性。所有团队成员都能就使用什么术语、如何描述流程和程序、如何将创建的模型应用于业务等问题达成共识。

为了让开发人员和领域专家处于同等地位,并简化他们之间的知识和专业知识的交流,DDD 方法要求团队就将在团队交流中使用并将在软件开发代码的多个阶段实施的标准术语和短语达成一致。

那么,让我们深入了解并仔细看看 DDD 模型。

通用语言

通用语言是一种集体语言,存储了软件开发团队使用的所有术语和关键短语。它是 DDD 最重要的模型之一。需要注意的是,通用语言不是技术术语,而是所有团队成员——领域专家、开发人员、业务分析师以及其他负责软件开发过程的专业人员——共同创建的真实语言。这个或那个团队成员的具体角色并不重要,因为他们都依赖通用语言来表达他们的想法。要创建通用语言,必须富有创造力,因为它像任何其他语言一样,会随着时间的推移而发展。此外,在软件开发过程开始时设定的原则和术语会过时,需要改进。结果,通用语言只保留最可靠和最高效的元素。Vaughn Vernon 建议我们使用以下方法

  1. 创建物理和概念域的图,并对其进行标记以表示特定的术语和操作。这类图不是正式的;这正是为什么您不需要使用正式建模(即 UML)。
  2. 创建包含简单定义和替代术语的主词汇表。这样,团队就创建了自己的通用语言,其中包含设定的术语、短语和描述。
  3. 创建文档文件而不是词汇表。这些文档将包括与软件本身相关的非正式图或关键术语。
  4. 与无法从一开始就掌握词汇表和文档文件的其他团队成员讨论现成的术语和短语。

由于代码的演变速度很快,您需要不断调整您的通用语言的当前版本。因此,如果您跟不上代码的步伐,就放弃图表、词汇表和其他文档文件。

每个开发人员需要记住的最重要的事情是,他或她需要仔细倾听领域专家,并尽可能多地获取关于主题的信息。与此同时,专家也应该注意开发人员所说和想要的。团队需要合作共同成长。

以下是 Vernon 的《领域驱动设计实现》中的几个例子。让我们想象一个团队正在开发一个描述如何以代码形式进行流感疫苗接种的模型。例如:“护士向患者接种 STD 流感疫苗。” 而代码可能会因团队合作/互动程度的不同而大相径庭。

显然,后一种选择要好得多。

至关重要的是要理解,某个术语的含义从开发和领域角度来看可能大相径庭。请记住:通用语言的参数是由一条明确的界线设定的。给定术语的上下文含义仅在该语言本身内才有效。

限界上下文

这条明确的概念界线被称为 BOUNDED CONTEXT(限界上下文),它是 DDD 中仅次于 UBIQUITOUS LANGUAGE(通用语言)的第二重要属性。两者相互依赖,并且无法分离存在。

因此,BOUNDED CONTEXT 是一个明确的界线——或者说,一个边界——它将领域包含在内,并将 UBIQUITOUS LANGUAGE 传递到软件模型中。以下是您还需要了解的关于 BOUNDED CONTEXT 的信息:

  • 为给定软件创建的每个限界上下文只能包含一个通用语言。
  • 限界上下文是相当有限的。BOUNDED CONTEXT 仅够容纳隔离域的 UBIQUITOUS LANGUAGE,而不能“跨越”边界。
  • 我们所说的“普遍存在”是指“无处不在”和“普遍”。基本上,这意味着所有团队成员都应该使用这种语言来表达他们对领域的想法。
  • 对于负责 BOUNDED CONTEXT 内项目的开发团队来说,这种语言只作为一个整体实体。
  • 如果有人试图依赖整个公司(或多个公司之间的操作)的 UBIQUITOUS LANGUAGE,那么失败是不可避免的。

例如,让我们看看一个给定的术语(例如“账户”)如何在不同的上下文中被使用:银行和小说。

银行:账户存储有关借记和贷记交易的所有数据,并从银行的角度显示客户的财务状况。→ 支票账户记录;储蓄账户记录等。

小说:账户是描述在给定时期内发生的一系列事件和经历的特定术语。→ 书《惊魂三日:埃佛勒斯峰灾难亲历记》。

基本上,您无法区分某个账户背后不同含义的界限。然而,如果您专注于特定的基于概念的容器,即相应的 BOUNDED CONTEXTs,您将能够做到这一点。

这些 BOUNDED CONTEXTs 与不同的 DOMAINs 相关。在下一个例子中,我们将在选定的 DOMAIN 中使用相同的名称。

在某些出版社,图书会经历一个特定的生命周期。在我们的例子中,这意味着一本书可能会经历不同的 CONTEXTs。例如:

  • 概念开发,向多家出版社推介
  • 与作者签订合同
  • 编辑
  • 图书排版和插图
  • 翻译成其他语言
  • 图书出版(印刷版和数字版)
  • 市场营销
  • 销售
  • 交付

因此,没有现实的方法可以提出一个放之四海而皆准的模型。DDD 方法是,为了避免图书生命周期不同阶段的任何问题,我们需要为每个阶段依赖单独的 BOUNDED CONTEXTs。如果我们选择了模型选项,它在每个 CONTEXT 中都会有所不同。但就我们的情况而言,每个 BOUNDED CONTEXT 的开发团队都了解其自身 CONTEXT 内关于图书的真正需求。总而言之,我们在同一个 DOMAIN 中使用不同的 BOUNDED CONTEXTs。

域、子域、核心域

DOMAIN 用于描述一家公司做什么以及它依赖于哪个学科领域。从事给定软件开发的每个开发人员都必须关注 DOMAIN。然而,还需要理解:当我们谈论领域建模时,我们必须只关注一个特定的 SUBDOMAIN。即使对于小型企业,我们也无法创建一个一刀切的模型。因此,根据其特征,将模型划分到给定 DOMAIN 的单独、逻辑上分离的 SUBDOMAINs 中非常重要。SUBDOMAINs 允许我们识别用于解决某个开发问题的 DOMAIN 的不同部分。

此外,识别所谓的 CORE DOMAIN 至关重要。CORE DOMAIN 是一个主要的 SUBDOMAIN。从战略上讲,CORE DOMAIN 是使一家公司区别于其他公司的原因。难怪大多数 DDD 项目都专注于 CORE DOMAIN。最优秀的开发人员和领域专家应该负责这个 SUBDOMAIN。建议将大部分资源投入到 CORE DOMAIN 中。这将有助于快速轻松地获得竞争优势。

如果您尝试为一个不属于 CORE DOMAIN 的特定重要功能建模,则创建特定的 SUPPORTING SUBDOMAIN。如果公司希望专注于某些特定功能,则可以要求创建 SUPPORTING SUBDOMAIN。然而,如果这些功能不是那么具体,并且需要为整个公司普遍使用,那么创建 GENERIC SUBDOMAIN 是有意义的。所有类型的 SUBDOMAINs 对企业的成功都很重要,但并非最关键。然而,最关键的是 CORE DOMAIN。如果它开发良好,将极大地推动业务发展。

理解 DOMAIN、SUBDOMAIN、CORE DOMAIN、GENERIC SUBDOMAIN 和 SUPPORTING SUBDOMAIN 的概念,并区分它们的能力是领域驱动设计的战略建模的关键。

任务空间和解决方案空间

每个 DOMAIN 都包含特定的任务空间和解决方案空间。任务空间使我们能够战略性地关注问题,而解决方案空间则处理软件解决这些问题的特定机制。任务空间是生成 CORE DOMAIN 所需的 DOMAIN 的一部分。它充当 CORE DOMAIN 和该核心需要利用的 SUBDOMAIN 的组合。解决方案空间是一个或多个 BOUNDED CONTEXTs,一套特定的软件模型。现成的 BOUNDED CONTEXT 是问题的实际解决方案,或该过程的现实呈现。

最佳选择是创建 SUBDOMAINs 和 BOUNDED CONTEXTs 之间的一对一对应关系。这样,我们可以结合任务空间和解决方案空间,并区分 DOMAIN 模型,以便为特定功能创建特定的域。需要注意的是,如果系统不是从头开始开发的,那么它就是一个所谓的 BIG BALL OF MUD,其中多个域与 BOUNDED CONTEXTs 相互交织。

在他的书中,Vaughn Vernon 描述了一个小型电子商务网站如何使用 CORE DOMAIN 模型。众所周知,任何电子商务企业都可以通过其员工依赖特定的销售预测机制来巩固其市场地位。为此,他们需要分析订单历史、供应、需求等。显然,这有助于商店减少开支,并将更多精力放在能赚钱的产品上。这就是 CORE DOMAIN 所应负责的。它为企业带来了差异化,因为它识别出了最有利的交易。因此,任务空间包括 CORE DOMAIN 和负责供应和需求的多个 SUBDOMAINs。您可以在下图看到它是如何工作的。

这只是 DOMAIN 的一部分,而不是公司运营的整个 DOMAIN(主题领域)。必须分析任务空间(CORE DOMAIN 加上 SUBDOMAINS)。您在图中看到的销售模型是专门为 CORE DOMAIN 开发的特定解决方案。域模型将在 BOUNDED CONTEXT 中创建,即在保证该电子商务网站最佳销售的上下文中。这个 BOUNDED CONTEXT 与一个 SUBDOMAIN 相关,而这个 SUBDOMAIN 又可以称为最佳销售的核心域。另一个称为产品购买上下文的 BOUNDED CONTEXT 被创建,以特别关注销售过程的技术细节,并将促进最佳销售上下文的开发过程。这些上下文与 ERP 系统的开放接口进行通信。销售上下文和 ERP 系统的销售模块结合在一起成为一个单一的销售支持子域。ERP 模块充当 GENERIC SUBDOMAIN,因为它可以在任何其他电子商务销售系统中进行切换。然而,当我们在销售子域中与销售上下文一起使用该模块时,它被用作 SUPPORTING SUBDOMAIN。如上图所示,最佳销售上下文还应与负责仓库中具体产品数量的销售供应上下文进行交互。它利用 ERP 系统中位于产品供应 SUPPORTING SUBDOMAIN 中的销售供应模块。ERP 应用程序由多个模块组成,我们将其逻辑上用作 SUBDOMAINs。这些是产品供应和产品销售的 SUBDOMAINs。供应和销售的模块和上下文被统一在 SUPPORTING SUBDOMAINs 的伞下。

简而言之,要评估给定项目的任务空间,我们需要:

  • 弄清楚战略性的 CORE DOMAIN 的样子(它包含什么,等等)
  • 识别哪些概念可以被视为 CORE DOMAIN 的组成部分
  • 指定所有 SUPPORTING 和 GENERIC DOMAINs。

任务空间评估和分析得越好,解决方案空间的分析就越容易。在此阶段,您必须考虑所有现有的系统和技术。此外,您需要考虑特定物理 ORGANIC CONTEXTs 的边界,这些边界非常适合应用的 UBIQUITOUS LANGUAGE。以下是您需要做的:

  • 分析软件设计重用的可能性
  • 指定您需要收集的资源以及您将使用的工具
  • 研究如何集成和使用这些资源和工具
  • 识别不同 BOUNDED CONTEXTs 的多个概念和数据如何重叠
  • 弄清楚哪些 BOUNDED CONTEXT 特征与 CORE DOMAIN 相关,以及哪些域可以用于其建模。

BOUNDED CONTEXT 不是 DOMAIN 模型唯一不可分割的一部分。尽管它始终是其最重要的组成部分。如果我们试图创建一个不考虑模型数据的图表或模板,它将被视为 BOUNDED CONTEXT 的一部分。然而,如果这个模型在项目级别上正确放置,它将与 BOUNDED CONTEXT 没有关系。CUSTOMER INTERFACE(模型面向用户的展示部分)也与 BOUNDED CONTEXT 相关。还可以创建面向服务的端点——它们也位于 BOUNDED CONTEXT 内。客户界面组件和服务导向的端点都被委托给 APPLICATION SERVICEs,这些服务在与给定模型的关系中用作 FRONT。这些 SERVICEs 也位于 BOUNDED CONTEXT 的边界内。

BOUNDED CONTEXTs 的大小可能不同。尽管如此,它们的主要目标是展示 UBIQUITOUS LANGUAGE 在 CONTEXT 中的丰富性。它需要存储提供 BOUNDED CONTEXT 内高质量建模所需的尽可能多的 DOMAIN 概念——不多也不少。为了避免遗漏任何实质性功能,您需要进行研究并确定最佳标准。这正是您需要依赖 CONTEXT MAP 的原因。让我们仔细看看它。

上下文映射

遵循 DDD 方法,每个负责软件开发的开发人员都需要创建自己的 CONTEXT MAP,以帮助识别团队所处的解决方案空间。该 MAP 由多个 BOUNDED CONTEXTs 组成,并描绘了它们之间的集成连接。它可能看起来像这样:

这个 CONTEXT MAP 显示了项目的当前状态,而不是它未来的样子。因此,记住在团队创建 CONTEXT MAP 时避免技术正式化非常重要。如果您过于关注细节,会阻碍过程。

尝试将完成此 MAP 区域的工作委托给专业人士。也就是说,多个上下文的边界将与您团队中的不同角色相吻合。专业人士协同工作,但划分了模型的通用上下文。

当 CONTEXT MAP 的初步版本准备好后,您可以添加其他详细信息,通过识别多个 CONTEXTs 之间的特定连接和“联系”来完成。以下是 BOUNDED CONTEXTs 和开发团队之间的一些连接:

  • PARTNERSHIP(伙伴关系)。当运行在两个不同 CONTEXTs 中的团队共同成功或失败时,它们会形成某种伙伴关系。这通常意味着它们需要相互合作,以识别它们如何更改接口以满足两个系统的需求。
  • SHARED KERNEL(共享核心)。模型和代码的通用/共享区域在多个区域之间建立了紧密的相互连接。此外,所有负责的团队都同意在模型域的子集之间划定明确的边界。核心应该相当小,并且不能在未通知其他团队的情况下进行编辑。特定命令的 UBIQUITOUS LANGUAGE 也需要达成一致。
  • CUSTOMER-SUPPLIER DEVELOPMENT(客户-供应商开发)。这可以在形成“下游-上游”关系时使用,并且上游团队必须考虑下游团队的优先级。
  • CONFORMIST(遵从者)。当存在“下游-上游”关系时,但上游团队不考虑下游团队的优先级,则存在这种关系。下游团队理解在不同上下文之间进行翻译相当困难,并允许上游团队完全控制其行为。
  • ANTICORRUPTION LAYER(反腐败层)。当管理和沟通模型与 SHARED KERNEL、PARTNERSHIP 或 CUSTOMER-SUPPLIER DEVELOPMENT 不一致时,翻译过程很复杂。下游客户应创建所谓反腐败层,以其域模型中使用的术语匹配其系统与上游系统的关系。这一层使用现有接口与另一个系统进行通信,而无需修改该系统。
  • OPEN HOST SERVICE(开放主机服务)。在这种情况下,我们识别出一个开放主机服务,该服务提供对系统的访问,就好像它是一组多个服务一样。如果出现新的集成需求,可以扩展或指定主机服务。
  • PUBLISHED LANGUAGE(已发布语言)。两种 BOUNDED CONTEXTs 的模型之间的翻译需要有自己的语言,即已发布语言。这种语言应用于通信和翻译有关某个域的特定信息。它可以从一种语言翻译成另一种语言。
  • SEPARATE WAYS(分道扬镳)。如果两组功能没有相互关联,您可以选择将它们完全分开。集成总是昂贵的,即使您保存了这两个功能集的连接,收益也会微不足道。
  • BIG BALL OF MUD(大泥球)。这个术语用于描述系统的某个部分,其中所有模型都混合在一起,所有边界都不清晰。如果您有这样的系统部分,请确保通过称其为 BIG BALL OF MUD 来区分它。

要了解更多关于上下文映射的信息,请查看 Alberto Brandolini 的文章 使用上下文映射进行战略领域驱动设计。但这是您可以做到的。首先,绘制一个简单的上下文地图,包含 BOUNDED CONTEXTs 之间的边界和连接。像这样:

当概念名称相似时,这两个上下文是不同的。例如,Web User Profiling 中的 Account 被用作用户的账户页面,包含登录名和密码。同时,PFM Application(个人理财管理)被用作一个特定的记录,其中存储了从银行角度来看客户的所有信息。有时,如上所述,相似的概念可以在完全不同的 CONTEXTs 中使用。这正是我们需要为它们识别特定模型的原因。


 
例如,PayeeAccount 和 Banking Account 之间没有区别,但它们的功能不同(没有余额表)。换句话说,有两个不同的上下文:(a)费用跟踪;(b)在线银行服务。

然后,您可以继续为不同上下文之间的连接添加更多详细信息。每个连接都有一个特定的方向。上游连接会影响下游连接(有时也会反向工作)。带有附加细节的 MAP 看起来像这样:

在线银行服务提供 API(OPEN HOST SERVICE 或 PUBLISHED LANGUAGE)。当此 API 发生更改时,其他上下文也应进行更改以与此 API 一致工作。通过这种方式,必须使用 ANTICORRUPTION LAYER 来避免不同上下文的混淆(仅修改 DOMAIN 模型)。

Web User Profiling 上下文被用作现成的外部模块,并且按原样提供。在这种情况下,应用了 CONFORMIST 关系(下游由上游控制)。

在费用跟踪上下文的情况下,依赖 PARTNERSHIP 是有意义的。有共同的目标和概念,但它们之间没有明确的关系方向。

这是一个相当简单的 CONTEXT MAP 示例。实际上,它们要复杂得多。

总结

在本文中,我回顾了领域驱动设计战略建模的主要术语,如:UBIQUITOUS LANGUAGE(通用语言)、BOUNDED CONTEXT(限界上下文)、DOMAIN(域)、SUBDOMAIN(子域)、CORE DOMAIN(核心域)、CONTEXT MAP(上下文映射)等。这些足以从战略上把握任何项目的全局,并弄清楚您或您的团队可以采取哪些步骤来取得成功。

当然,我很难声称本文包含了您需要了解的关于 DDD 的所有信息。如果您想了解更多,请查看我上面提到的 Vaughn Vernon 的书。此外,还有许多社区热衷于 DDD(例如这个)。在那里您可以提问,或为其他社区成员提供帮助。

在下一篇文章中,我将深入探讨 ENTITY(实体)、VALUE OBJECT(值对象)、AGGREGATE OBJECT(聚合对象)等建模内容。感谢您的时间!

© . All rights reserved.