你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

使用域分析对微服务进行建模

微服务的最大挑战之一是定义各个服务的边界。 一般的规则是服务应该只做“一件事”,但是,实践这条规则需要经过认真的考虑。 没有任何机械性的流程可以生成“适当的”设计。 必须深入考虑业务域、要求和目标。 否则,最终可能得到一个杂乱无章的设计,它呈现一些不需要的特征,例如隐藏服务之间的依赖关系、紧密耦合,或者界面的设计不佳。 本文介绍设计微服务的域驱动方法。

本文以无人机交付服务为例进行讨论。 可以在此处阅读有关方案和相应参考实现的详细信息。

简介

应该围绕业务功能而不是数据访问或消息传递等水平层来设计微服务。 此外,微服务应具有低耦合高内聚的特点。 如果在更新一个服务时无需同时更新其他服务,则该微服务是低耦合的。 如果微服务的职责单一且定义完善,例如管理用户帐户或跟踪交付历史记录,则它是高内聚的。 服务应该封装领域知识,使这些领域知识对于客户端而言处于抽象状态。 例如,客户端应该能够在无需知晓派遣算法或如何管理无人机群的情况下安排无人机。

领域驱动设计 (DDD) 提供一个框架,可以让你顺利访问一组设计完善的微服务。 DDD 包括两个不同的阶段:战略和战术。 在 DDD 的战略模式中,可以定义系统的大规模结构。 战略模式有助于确保体系结构专注于业务功能。 策略性 DDD 提供一组可用于创建域模型的设计模式。 这些模式包括实体、聚合和领域服务。 借助这些战术模式,可以设计低耦合高内聚的微服务。

域驱动设计 (DDD) 流程图

本文和下一篇文章将引导你完成以下步骤,并将其应用到无人机交付应用程序:

  1. 我们首先分析业务领域,以了解应用程序的功能要求。 该步骤输出领域的非正式说明,可将其优化成更正式的一组领域模型。

  2. 接下来,定义领域的边界上下文。 每个边界上下文包含一个领域模型,该模型表示较大应用程序的特定子域。

  3. 在边界上下文中,应用战术 DDD 模式以定义实体、聚合和域服务。

  4. 使用前一步骤的结果可以标识应用程序中的微服务。

本文介绍前三个步骤,并重点讨论 DDD。 下一篇文章将介绍如何标识微服务。 但是,请务必记住,DDD 是迭代的持续过程。 服务边界不是一成不变的。 随着应用程序的演变,你可以决定将某个服务分解成多个较小服务。

注意

本文章未完整介绍全面的域分析。 我们有意精简了示例,以演示要点。 有关 DDD 的更多背景信息,我们建议阅读 Eric Evans 的 Domain-Driven Design(领域驱动设计),该书籍首次引入了该术语。 另一项优秀的参考资源是 Vaughn Vernon 撰写的《实现领域驱动设计》。

场景:无人机交付

Fabrikam, Inc. 正在推出无人机交付服务。 该公司经营无人机群。 各商家注册该服务,用户可以请求无人机收取要交付的商品。 当客户安排取件时,后端系统会分配一架无人机,并将估计的交付时间告知用户。 在交付过程中,客户可以通过持续更新的 ETA 跟踪无人机的位置。

此方案涉及到一个相当复杂的域。 部分业务难题包括安排无人机、跟踪包裹、管理用户帐户,以及存储和分析历史数据。 此外,Fabrikam 希望快速投放市场和扩张,同时添加新功能。 该应用程序需要以云的规模运行,并附带较高的服务级别目标 (SLO)。 此外,Fabrikam 预期系统的不同部件在数据存储和查询方面具有截然不同的要求。 所有这些考虑因素促使 Fabrikam 为无人机交付应用程序选择了微服务体系结构。

分析领域

借助 DDD 方法可以设计微服务,使每个服务原生都能符合业务功能要求。 此方法有助于避免组织边界或技术选择左右你的设计。

在编写任何代码之前,需要获取所创建的系统的鸟瞰图。 DDD 模式首先对业务进行领域建模,然后创建领域模型。 领域模型是业务领域的抽象模型。 它可以提取和组织领域知识,并为开发人员和领域专家提供通用语言。

首先,映射所有业务功能及它们之间的连接。 这可能是涉及到领域专家、软件架构师和其他利益干系人的协作性工作。 无需使用任何特定的形式。 可以直接草绘或者在白板上绘制关系图。

在填充关系图时,可以开始标识离散的子域。 哪些功能密切相关? 哪些功能是业务的核心?哪些功能提供辅助服务? 什么是依赖项关系图? 在此初始阶段,不需要考虑技术或实施细节。 也就是说,应该注意应用程序要在哪个位置与 CRM、付款处理或计费系统等外部系统集成。

示例:无人机交付应用程序

完成一些初始域分析之后,Fabrikam 团队绘制了一份描绘无人机交付域的草图。

无人机交付域图

  • “交货”位于关系图的中心,因为它是业务的核心。 关系图中的其他任何元素都是为了支持此功能。
  • “无人机管理”也是业务的核心。 与无人机管理密切相关的功能包括无人机维修,以及使用预测分析来预测无人机何时需要检修和维护。
  • ETA 分析提供取件和交货的估计时间。
  • 如果包裹无法完全由无人机交货,则应用程序可以通过第三方运输来安排替代的运输方式。
  • 无人机共享是核心业务的可能扩展。 公司的无人机在某些时段可能容量过剩,在这种情况下,可以出租无人机,以避免闲置。 初始版本未包括此功能。
  • 视频监督是公司以后可以拓展到的另一个领域。
  • 用户帐户开票呼叫中心是支持核心业务的子域。

请注意,在此流程的此阶段,我们尚未做出有关实施或技术的任何决策。 某些子系统可能涉及到外部软件系统或第三方服务。 即便如此,应用程序也需要与这些系统和服务进行交互,因此,必须将它们包含在领域模型中。

注意

如果应用程序依赖于外部系统,则存在一种风险:外部系统的数据架构或 API 会渗入应用程序,最终暴露了体系结构设计。 不遵循最佳实践,并使用复杂数据架构或过时 API 的旧式系统尤其如此。 在这种情况下,必须在这些外部系统与应用程序之间妥善定义边界。 为此,请考虑使用 Strangler Fig 模式防损层模式

定义边界上下文

领域模型将包含现实世界中事物的表示形式 - 用户、无人机、包裹,等等。 但这并不意味着系统的每个部分都需要对相同的事物使用相同的表示形式。

例如,处理无人机维修和预测分析的子系统需要呈现无人机的许多物理特征,例如其维护历史记录、里程、机龄、型号、性能特征,等等。 但是,在安排交付时,我们并不需要关心这些方面。 计划子系统只需知道无人机是否可用,以及取件和交货的 ETA。

如果尝试为这两个子系统创建了单个模型,则会不必要地增大复杂性。 此外,模型会更难得到发展,因为任何更改都需要满足处理不同子系统的多个团队的要求。 因此,更好的做法通常是设计不同的模型,用于在两种不同的上下文中呈现相同的真实实体(在本例中为无人机)。 每个模型仅包含其特定上下文中相关的功能和属性。

在此场合下,“边界上下文”的 DDD 概念可以派上用场。 边界上下文只是应用特定领域模型的领域中的边界。 在上图中,我们可以根据各种功能是否共享单个领域模型将功能分组。

边界上下文示意图

边界上下文不一定相互独立。 在此图中,连接边界上下文的实线表示两个边界上下文交互的位置。 例如,“交货”依赖于“用户帐户”来获取有关客户的信息,并依赖于“无人机管理”来安排机群中的无人机。

Domain Driven Design(领域驱动设计)一书中,Eric Evans 描述了当某个领域模型与另一个边界上下文交互时,保持该模型完整性的多种模式。 微服务的主要原则之一是服务通过完善定义的 API 进行通信。 此方法对应于两种模式,即 Evans 所说的“开放主机服务”和“发布语言”。 “开放主机服务”的思路是子系统针对与它通信的其他子系统定义一个正式协议 (API)。 “发布语言”扩展了这种思路,它以其他团队可以用来编写客户端的形式发布 API。 为微服务设计 API 一文介绍了如何使用 OpenAPI 规范(以前称为 Swagger)来为 REST API 定义语言无关的接口说明(以 JSON 或 YAML 格式表示)。

本教程的余下部分侧重于“交货”边界上下文。

后续步骤

完成域分析后,下一步是应用战术 DDD,以更精确地定义域模型。