标识每个微服务的域模型边界

提示

此内容摘自电子书《适用于容器化 .NET 应用程序的 .NET 微服务体系结构》,可在 .NET 文档上获取,也可作为免费可下载的 PDF 脱机阅读。

.NET Microservices Architecture for Containerized .NET Applications eBook cover thumbnail.

在为每个微服务标识模型边界和大小时,虽然应尽可能倾向小型微服务,但目标并不是尽可能达到最细粒度分离。 相反,目标应在域知识指导下实现最有意义的分离。 重点不在于大小,而在于业务功能。 此外,如果基于大量依赖关系的应用程序的某一特定区域需要清除内聚,这也表明需要单个微服务。 内聚是一种标识如何将微服务分离或组合的方法。 最终,当获得关于域的详细知识时,应以迭代方式调整微服务的大小。 查找适当的大小不是一个单步过程。

Sam Newman 是一位公认的微服务促进者以及《构建微服务》一书的作者,他强调应该按照前面介绍的界定的上下文 (BC) 模式(域驱动设计的一部分)来设计微服务。 有时,BC 可以由几个物理服务组成,但反之则不然。

具有特定域实体的域模型应用于具体的 BC 或微服务中。 BC 划分了域模型的适用性,并使开发人员团队成员对哪些必须具有内聚和哪些可以独立开发有了清晰和共同的理解。 这些都是微服务的相同目标。

另一个通知设计选择的工具是康威定律,它规定应用程序应反映生成它的组织的社交边界。 但有时恰恰相反 — 公司的组织是由软件组成的。 可能需要方向吸取康威定律,按照想要公司进行组织的方式生成边界,倾向于业务流程咨询。

若要标识有界上下文,可以使用称为上下文映射模式的 DDD 模式。 通过上下文映射,标识应用程序中的各种上下文及其边界。 例如,对于每个小型子系统来说,有一个不同的上下文和边界是很常见的。 上下文映射是一种定义和明确域之间的这些边界的方法。 BC 具有自治性,它包含单个域的详细信息(比如域实体)并定义了与其他 BC 的集成契约。 这类似于微服务的定义:具有自治性,实现特定的域功能,并且必须提供接口。 因此上下文映射和界定的上下文模式是标识微服务的域模型边界的好方法。

在设计大型应用程序时,将看到其域模型可能存在碎片,例如,目录域中的域专家将在目录和清单域中命名实体,该行为不同于传送域专家的操作。 或者,对于想要存储客户的每个细节的 CRM 专家,用户域实体的大小和属性数目可能与只需要部分客户数据的订购域专家要求不同。 很难在与大型应用程序相关的所有域中消除所有域术语的歧义。 但最重要的是不应尝试统一术语。 而是接受每个域所带来的差异和丰富性。 如果尝试为整个应用程序创建一个统一的数据库,那么尝试统一词汇会很繁琐,而且对任何多个域专家来说都是不正确的。 因此,BC(作为微服务实现)将帮助阐明在哪里可以使用特定域术语,以及在哪里需要拆分系统,并使用不同的域创建其他 BC。

如果域模型之间的强关系很少,那么每个 BC 和域模型的边界和大小正确,并且在执行典型的应用程序操作时通常不需要合并来自多个域模型的信息。

对于每个微服务的域模型应该有多大这个问题,最佳答案是:它应该有一个尽可能独立的自治 BC,这样就可以工作而不必不断切换到其他上下文(其他微服务的模型)。 在图 4-10 中可以看到多个微服务(多个 BC)如何生成自己的模型,以及如何定义其实体,具体取决于应用程序中每个标识域的特定要求。

Diagram showing entities in several model boundaries.

图 4-10。 标识实体和微服务模型边界

图 4-10 演示了与联机会议管理系统相关的示例方案。 相同实体会显示为“Users”、“Buyers”、“Payers”和“Customers”,具体取决于有界上下文。 已经根据域专家定义的域确定了几个可作为微服务实现的 BC。 如你所见,有些实体只存在于单个微服务模型中,比如付款微服务中的付款。 这些将易于实现。

但是,也可能有一些实体具有不同的形状,但在来自多个微服务的多个域模型中共享相同标识。 例如,在会议管理微服务中标识用户实体。 具有相同标识的相同用户在订购微服务中名为“购买者”或在付款微服务中名为“付款者”,甚至在客户服务微服务中名为“客户”。 这是因为,具体取决于每个域专家使用的通用语言,用户可能有不同的视角,甚至具有不同的属性。 名为“会议管理”的微服务模型中的用户实体可能具有其大部分个人数据属性。 但是,在微服务支付中以“付款者”形式或者在微服务客户服务中以“客户”形式的相同用户可能不需要相同的属性列表。

图 4-11 说明了类似的方法。

Diagram showing how to decompose a data model into multiple domain models.

图 4-11。 将传统数据模型分解为多个域模型

在有界上下文之间分解传统数据模型时,可以使不同实体共享相同标识(购买者也是用户),在每个有界上下文中具有不同特性。 可以看到用户是如何作为用户实体出现在会议管理微服务模型中的,并且在定价微服务中也以购买者实体的形式出现,当用户实际上是购买者时,具有备用属性或用户详细信息。 每个微服务或 BC 可能不需要与用户实体相关的所有数据,而只是其中的一部分,具体取决于要解决的问题或上下文。 例如,在定价微服务模型中,不需要用户的地址或名称,只需 ID(作为标识)和状态,这将影响在为每个购买者的席位定价时的折扣。

在每个域模型中,席位实体的名称都相同,但属性不同。 然而,席位共享具有相同 ID 的标识,就像用户和购买者一样。

基本上,在多个服务(域)中存在用户的共享概念,都共享该用户的标识。 但在每个域模型中,可能存在有关该用户实体的其他或不同的详细信息。 因此,需要有一种方法将用户实体从一个域(微服务)映射到另一个域。

不与相同的属性数目跨域共享相同的用户实体有几个好处。 一个好处是减少重复项,以便微服务模型没有不需要的任何数据。 另一个好处是拥有一个主微服务,它拥有每个实体的特定类型的数据,以便对该类型数据的更新和查询只能由该微服务驱动。