你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
微服务的最大挑战之一是定义各个服务的边界。 一般规则是,服务应该只执行一件事,但将规则付诸实践需要仔细思考。 没有产生正确设计的机械过程。 必须深入了解业务领域、要求、体系结构特征(也称为 非功能要求)和目标。 否则,最终可能得到一个杂乱无章的设计,它呈现一些不需要的特征,例如隐藏服务之间的依赖关系、紧密耦合,或者界面的设计不佳。 本文介绍设计微服务的域驱动方法。 评估服务边界是一项针对不断变化的工作负荷的持续性工作。 有时,评估会导致重新定义的现有边界定义,这些定义需要更多应用程序开发以适应这些更改。
本文以无人机交付服务为例进行讨论。 有关方案的详细信息和相应的参考实现,请参阅 设计微服务体系结构。
简介
应该围绕业务功能而不是数据访问或消息传递等水平层来设计微服务。 此外,它们应具有低耦合和高功能内聚的特点。 如果在更新一个服务时无需同时更新其他服务,则该微服务是低耦合的。 如果微服务的职责单一且定义完善,例如管理用户帐户或跟踪交付历史记录,则它是高内聚的。 服务应该封装领域知识,使这些领域知识对于客户端而言处于抽象状态。 例如,客户应该能够在无需了解调度算法或无人机群管理细节的情况下安排无人机。 必须为每个微服务定义体系结构特性,以匹配其关注的领域,而不是为整个系统进行定义。 例如,面向客户的微服务可能需要具有性能、可用性、容错、安全性、可测试性和敏捷性。 后端微服务可能需要只有容错和安全性。 如果微服务之间有同步通信,它们之间的依赖性往往会产生共享相同体系结构特性的需求。
领域驱动设计 (DDD) 提供一个框架,可以让你顺利访问一组设计完善的微服务。 DDD 包括两个不同的阶段:战略和战术。 在战略 DDD 中,定义系统的大规模结构。 战略领域驱动设计 (DDD) 有助于确保架构专注于业务能力。 策略性 DDD 提供一组可用于创建域模型的设计模式。 这些模式包括实体、聚合和领域服务。 这些战术模式可帮助你设计既松散耦合又有凝聚力的微服务。
本文和下一篇文章将引导你完成以下步骤,并将其应用到无人机交付应用程序:
我们首先分析业务领域,以了解应用程序的功能要求。 该步骤输出领域的非正式说明,可将其优化成更正式的一组领域模型。
接下来,定义领域的边界上下文。 每个边界上下文包含一个领域模型,该模型表示较大应用程序的特定子域。
在边界上下文中,应用战术 DDD 模式以定义实体、聚合和域服务。
使用前一步骤的结果可以标识应用程序中的微服务。
本文介绍前三个步骤,并重点讨论 DDD。 下一篇文章将介绍如何标识微服务。 但是,请务必记住,DDD 是迭代的持续过程。 服务边界不是一成不变的。 随着应用程序的发展,你可能会决定将一个服务分解成几个较小的服务。
注意
本文章未完整介绍全面的域分析。 我们有意精简了示例,以演示要点。 有关 DDD 的更多背景信息,我们建议阅读 Eric Evans 的 Domain-Driven Design(领域驱动设计),该书籍首次引入了该术语。 另一项优秀的参考资源是 Vaughn Vernon 撰写的《实现领域驱动设计》。
场景:无人机交付
Fabrikam, Inc. 正在推出无人机交付服务。 该公司管理无人机飞机群。 各商家注册该服务,用户可以请求无人机收取要交付的商品。 当客户安排取件时,后端系统会分配一架无人机,并将估计的交付时间告知用户。 在交付过程中,客户可以通过持续更新的 ETA 跟踪无人机的位置。
此场景包括一个复杂度较高的领域。 一些关键业务问题包括计划无人机、跟踪包、管理用户帐户以及存储和分析历史数据。 Fabrikam 还旨在快速上市,并快速迭代,添加新功能和功能。 应用程序必须以云规模运行,并满足高服务级别目标。 此外,Fabrikam 还要求系统的不同部分对数据存储和查询有不同的要求。 这些注意事项导致 Fabrikam 采用无人机交付应用程序的微服务体系结构。
分析领域
DDD 方法可帮助你设计微服务,以便每个服务自然适配功能业务需求。 此方法有助于避免组织边界或技术选择左右你的设计。
在编写任何代码之前,应对生成的系统有一个高级了解。 DDD 模式首先对业务进行领域建模,然后创建领域模型。 领域模型是业务领域的抽象模型。 它提取和组织域知识,并为开发人员和域专家提供公共语言。
首先映射所有业务功能及其连接。 这项工作可以是包括领域专家、软件架构师和其他利益干系人在内的协作。 无需使用任何特定的形式。 可以直接草绘或者在白板上绘制关系图。
填写关系图时,可能会开始识别离散子域。 哪些功能密切相关? 哪些功能是业务的核心,哪些功能提供辅助服务? 依赖项关系图是什么? 在此初始阶段,不需要考虑技术或实施细节。 也就是说,应注意应用程序需要与外部系统集成的位置,例如客户关系管理、付款处理或计费系统。
示例:无人机交付应用程序
完成一些初始域分析之后,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,以更精确地定义域模型。