分析应用程序并确定分解边界
若要将其应用程序移动到微服务体系结构,Fabrikam 需要评估其当前应用程序并确定每个微服务的范围和边界。 对于此评估,他们将使用 域驱动设计 (DDD)框架。 让我们看看它们如何将它应用于其应用程序。
注释
本文章未完整介绍全面的域分析。 我们故意将示例简短地说明要点。 有关 DDD 的详细信息,请参阅本模块末尾摘要中的“了解详细信息”部分。
什么是域驱动设计?
DDD 是 2005 年 Erik Evans 在 2005 年书 《Domain-Driven 设计:解决软件核心的复杂性》中最初引入的系统设计的方法。 此方法包括三个关键元素:
- 专注于核心域和域逻辑。
- 基于领域模型进行结构设计。
- 推动技术团队与业务合作伙伴之间的迭代协作,不断改进系统。
DDD 提供了一个框架,可让你充分利用一组设计良好的微服务。 它有两个不同的阶段, 战略 和 战术。 在战略 DDD 中,定义系统的大规模结构。 战略领域驱动设计 (DDD) 有助于确保架构专注于业务能力。 策略性 DDD 提供一组可用于创建域模型的设计模式。 这些模式包括实体、聚合和领域服务。 这些战术模式可帮助你设计松散耦合和凝聚力的微服务。
在 DDD 的战略阶段,绘制业务领域的关系图,并定义领域模型的边界上下文。 在战术 DDD 阶段,需要更精确地定义领域模型。 战术模式在单个边界上下文中应用。 在微服务体系结构中,我们对实体和聚合模式感兴趣。 应用这些模式有助于我们确定应用程序中服务的自然边界。 作为一般原则,微服务应不小于聚合,且不大于边界上下文。
概括而言,可以将此过程分为四个步骤:
- 分析业务域以了解应用程序的功能要求。 该步骤输出领域的非正式说明,可将其优化成更正式的一组领域模型。
- 定义域的边界上下文。 每个边界上下文包含一个领域模型,该模型表示较大应用程序的特定子域。
- 在边界上下文中,应用战术 DDD 模式以定义实体、聚合和域服务。
- 使用上一步的结果标识应用程序中的微服务。
让我们进一步了解每个步骤中会发生什么情况。
分析业务域
DDD 首先对业务域建模和创建域模型。 领域模型是业务领域的抽象模型。 它提取和组织域知识,并为开发人员和域专家提供公共语言。
首先,映射所有业务功能及它们之间的连接。 此分析是一项协作工作,涉及领域专家、软件架构师和其他利益干系人。 无需使用任何特定的形式。 草图或将其绘制在白板上。
填写关系图时,可能会开始识别离散子域。 哪些功能密切相关? 哪些功能是业务的核心?哪些功能提供辅助服务? 什么是依赖项关系图? 在此初始阶段,不需要考虑技术或实施细节。 也就是说,应注意应用程序需要与外部系统(如 CRM、付款处理或计费系统)集成的位置。
定义边界上下文
域模型包括世界上真实事物的模型,如用户、无人机和包裹。 但这并不意味着系统的每个部分都需要对相同的事物使用相同的表示形式。
例如,处理无人机修复和预测分析的子系统需要表示无人机的许多物理特征。 这些特征包括维护历史记录、里程、年龄、型号和性能详细信息。 但是,在安排交付时,我们并不需要关心这些方面。 计划子系统只需知道无人机是否可用以及预计到达时间(ETA)才能进行取件和交付。
如果尝试为这两个子系统创建单个模型,则比我们需要的要复杂得多。 模型随着时间推移而发展也变得更加困难,因为任何更改都需要满足多个在单独的子系统上运行的团队。 通常最好在两个不同的上下文中设计表示相同真实实体(在本例中为无人机)的单独模型。 每个模型仅包含其特定上下文中相关的功能和属性。
此方法是 DDD 概念的限界上下文所发挥作用的地方。 边界上下文只是应用特定领域模型的领域中的边界。 在上图中,我们可以根据各种函数是否共享单个域模型对功能进行分组。
定义实体、聚合和服务
在战术 DDD 阶段,需要更精确地定义领域模型。 战术模式在单个边界上下文中应用。 在微服务体系结构中,我们对实体和聚合模式感兴趣。 应用这些模式有助于我们确定应用程序中服务的自然边界。 作为一般原则,微服务应不小于聚合,且不大于边界上下文。
有几种战术 DDD 模式需要考虑:
- 实体:实体是具有一段时间内保留的唯一标识的对象。 例如,在银行应用程序中,客户和帐户是实体。
- 值对象:值对象没有标识。 其属性的值定义它,它是不可变的。 值对象的典型示例包括颜色、日期时间和货币值。
- 聚合:聚合定义一个或多个实体的一致性边界。 聚合的作用是为事务不变性建模。 现实世界中的事物具有复杂的关系。 客户创建订单,订单包含产品,产品有供应商,等等。 如果应用程序修改了多个相关对象,它如何保证一致性? 如何跟踪并实施不变性?
- 域和应用程序服务:在 DDD 术语中,服务是一个实现某些逻辑且不保留任何状态的对象。 Evans 区分域服务,这些服务封装了域逻辑,以及提供技术功能的应用程序服务。 应用程序服务通常包括技术功能,例如用户身份验证或发送短信。 领域服务通常用于对跨多个实体的行为建模。
- 域事件:域事件可用于在发生某种情况时通知系统的其他部分。 顾名思义,领域事件应该表示领域中发生的某些情况。 例如,“在表中插入了记录”不是领域事件。 “已取消交付”是领域事件。 领域事件与微服务体系结构密切相关。 由于微服务为分发式且不共享数据存储,领域事件可为微服务提供相互协调的途径。
在其系统中,Fabrikam 开发团队确定了以下实体:
- 交货
- 包装
- 无人机
- 帐户
- 确认
- 通知
- 标记
前四个实体对象(交付、包裹、无人机和账户)都是表示事务一致性边界的概念聚合。 “确认”和“通知”是“交付”的子实体。 标签是包的子实体。
此设计中的值对象包括 Location、ETA、PackageWeight 和 PackageSize。
有两个领域事件:
- 当无人机正在飞行时,无人机实体发送无人机Status 事件,用于描述无人机的位置和状态,如飞行中和降落。
- 每当传递阶段发生更改时,传递实体就会发送 DeliveryTracking 事件。 这些事件包括 DeliveryCreated、DeliveryRescheduled、DeliveryHeadedToDropoff 和 DeliveryCompleted。
请注意,这些事件描述领域模型中有意义的事物。 它们描述了有关域的内容,并且与特定的编程语言构造无关。
开发团队还确定了另一个功能领域,但该功能领域并不与前面所述的任何实体紧密相关。 系统的某个部分必须协调有关安排或更新交付的所有步骤。 开发团队向设计添加了两个域服务。 调度器协调步骤。 监督员监控每个步骤的进度,以检测是否有步骤失败或超时。
标识微服务
现在,我们可以从领域模型转到应用程序设计。 下面是一个用于从域模型派生微服务的方法。
- 从限定上下文开始。 通常,微服务中的功能不应跨多个限定上下文。 根据定义,边界上下文标记特定领域模型的边界。 如果微服务将不同的域模型混合在一起,则表明可能需要优化域分析。
- 接下来,查看领域模型中的聚合。 聚合通常是微服务的适当候选项。 设计良好的聚合显示了设计良好的微服务的许多特征:
- 聚合派生自业务要求,而不是技术问题,例如数据访问或消息传送。
- 聚合应具有较高的功能内聚性。
- 聚合是持久性的边界。
- 聚合应为松散耦合。
- 域服务也是微服务的适当候选项。 域服务是跨多个聚合的无状态操作。 典型的示例是涉及多个微服务的工作流。 稍后,我们在无人机交付应用程序中看到了域服务的示例。
- 最后,考虑非功能要求。 分析团队规模、数据类型、技术、可伸缩性要求、可用性要求和安全要求等因素。 这些因素可能导致将微服务进一步分解为两个(或更多)较小的服务,或者执行相反作,并将多个微服务合并为一个。
请务必务实并记住域驱动设计是一个迭代过程。 如果有疑问,可以从更粗粒度的微服务入手。 将微服务拆分为两个较小的服务比在多个现有微服务中重构功能更容易。
将域驱动设计应用于无人机应用程序
对于 Fabrikam 的应用程序,所有这些服务都驻留在其现有的整体应用程序中。 确定应用程序可在何处分解为微服务后,它们将开始使用包服务。
包服务当前有一个专门开发团队,表现出了与可伸缩性相关的性能问题,是开始分解应用程序的有利候选位置。