InfoQ如何基于 DDD 构建微服务?( 三 )


另一个迹象表明 , 同一个模型在不同的上下文中传播 , 所有这些模型都直接与单个支付网关相集成 , 并且彼此执行相同的操作 。
InfoQ如何基于 DDD 构建微服务?
本文插图
图 4:定义错误的上下文映射
重新定义服务边界
在上面的设计中有一些非常明显的问题(图 4) 。 支付聚合是多个上下文的一部分 。 在各种服务之间强制执行不变性和一致性是不可能的 , 更不用说这些服务之间的并发问题了 。 例如 , 如果在订单服务尝试按之前提交的付款方式进行结算的过程中 , 联络中心更改了与订单关联的付款方式会发生什么情况 。 另外 , 请注意 , 支付网关中的任何更改都将迫使对多个服务进行更改 , 可能会涉及到多个团队 , 因为它们共同拥有这些上下文 。
通过一些调整并将聚合与正确的上下文对齐 , 我们就可以更好地表示这些子域了(图 5) 。 需要进行很多的更改 。
我们来看下更改的点:
支付聚合有了一个新家——支付服务 。 该服务还从其他需要支付服务的服务中提取了支付网关 。 由于单个界限上下文现在拥有了单个聚合 , 所以不变量很容易管理;所有事务都在同一个服务的边界内进行 , 从而避免了任何并发问题 。
支付聚合使用了反腐层(ACL)将核心领域模型与支付网关的数据模型隔离开来 , 后者通常是由第三方提供的 , 可能会发生变化 。 在以后的文章中 , 我们将深入研究基于“端口和适配器”模式的应用程序设计 。 ACL 层通常包含将支付网关的数据模型转换为支付聚合数据模型的适配器 。
购物车服务通过直接调用 API 的方式来调用支付服务 , 因为当客户在网站上购物时 , 购物车服务需要完成支付授权 。
记录订单和支付服务之间的交互作用 。 订单服务发出一个域事件(稍后会在本文中对此进行详细介绍) 。 支付服务监听此事件并完成订单的结算
联络中心服务可能有许多聚合 , 但我们只对该用例中的订单聚合感兴趣 。 当更改付款方式时 , 此服务发出一个事件 , 支付服务将通过以下方式对此事件做出响应:将先前使用的信用卡撤销 , 再处理新的信用卡 。
InfoQ如何基于 DDD 构建微服务?
本文插图
图 5:重新定义的上下文映射
通常 , 单体或遗留应用程序有许多聚合 , 且边界重叠 。 创建这些聚合及其依赖关系的上下文映射 , 将有助于我们理解从这些单体应用中获取任何新微服务的轮廓 。 请记住 , 微服务架构的成败取决于聚合之间的低耦合以及聚合之内的高内聚 。
还需要注意的是 , 界限上下文本身就是合适的内聚单元 。 即使上下文有多个聚合 , 也可以将整个上下文及其聚合组成单个微服务 。 我们发现这种启发式方法对于有些模糊的领域特别有用 , 比如组织正在涉足的新业务领域 。 我们可能对分离的正确边界没有足够的了解 , 并且任何过早的聚合分解都可能导致昂贵的重构 。 试想一下 , 由于数据迁移 , 不得不将两个数据库合并为一个 , 因为我们偶然发现两个聚合属于同一类 。 但是要确保这些聚合通过接口是充分隔离的 , 这样它们就不知道彼此的复杂细节了 。
事件风暴:另一种识别服务边界的技术
事件风暴(Event Storming)是识别系统中聚合(以及微服务)的另一种必不可少的技术 。 它是一个非常有用的工具 , 既可用于分解单体应用 , 也可用于设计复杂的微服务生态系统 。 我们已经使用这种技术分解了一个复杂的应用程序 , 并打算写一篇单独的文章来介绍我们的事件风暴经验 。 在本文中 , 我们只给出一个快速的高层概述 。
简而言之 , 事件风暴是在应用程序团队(这里 , 指单体)中进行的头脑风暴 , 以识别系统中发生的各种领域事件和流程 。 团队还需确定这些事件影响的总和或模型 , 以及由此产生的任何后续影响 。 当团队做这个头脑风暴时 , 他们将识别不同的重叠概念、模棱两可的领域语言和相互冲突的业务流程 。 他们对相关的模型进行分组 , 重新定义聚合并识别重复的流程 。 随着这些工作的进行 , 这些聚合所属的界限上下文变得清晰起来 。 如果所有团队都在同一个房间(物理或虚拟)里 , 并开始在 Scrum 风格的白板上绘制事件、命令和流程的映射 , 那么事件风暴研讨就会非常有用 。 在本练习结束时 , 通常会产出如下成果: