在我的最新项目中,我致力于采用事件驱动架构来重构一个复杂的代码体系。我发现实现真正的解耦比我预想的要困难得多。我打算在我的博客上分享我的实践经历和所汲取的教训。
事件驱动架构的承诺与探索
我从一个服务开始着手,这个服务包含超过2000行的代码,负责更新多个组件中的多个实体。这些实体内部和外部都有复杂的条件和逻辑,一个组件的变化可能会以不可预测的方式影响其他组件。在缺乏足够的测试代码来覆盖服务类各种情况的情况下,这更加剧了问题的复杂性。
这里我们遇到了事件驱动模式的概念。该模式的核心理念是明确分离关注点,避免意外变化。每个用例只需专注于更新一个实体并发布相应的事件。事件监听器则监听感兴趣的事件,然后调用相应的用例来更新相关实体。目标是创建一个松耦合的系统,使各个组件能够独立发展。
双向交流的难题
事件驱动的方法最初看起来很有前景,但很快就开始面临挑战。事件驱动架构的一个主要限制是它不适用于双向通信。事件的发布和订阅本质上是单向的,即从A到B。这使得在事件驱动系统中,B无法轻易地向A反馈,这在某些情况下会引发问题。
以订单服务和支付服务的互动为例。当订单被提交时,订单服务(A)会向支付服务(B)发送OrderPlaced事件。如果支付服务在处理支付过程中遇到问题(例如用户的信用卡被拒绝),它需要告知订单服务出现问题。在事件驱动系统中,由于B无法轻易地向A反馈,这可能导致用户无法及时收到关于支付问题的反馈。
为了解决这一问题,我们可以考虑为支付服务B设置一个单独的端点,如通过Webhook向订单服务A传达支付结果。但这样做会增加系统的复杂性,特别是当B是外部服务时,通过新的端点同步结果可能并不总是可行。这样一来,A和B之间的互动变得更加复杂,破坏了事件驱动系统最初的简洁和优雅。
事务一致性与最终一致性的挑战
在思考时采用事务性的方式与采用最终一致性的方式是完全不同的。在最终一致性的系统中,服务A假设在向服务B发布事件后,服务B最终会完成任务,但实际上这远比听起来复杂。
以订单服务和CRM(客户关系管理服务)为例。订单服务(A)负责处理订单并在订单完成后向用户发送通知。CRM(B)负责管理会员奖励,这些奖励会与商品一起寄送给常客。在事件驱动的系统中,当订单完成并需要提及已发出的会员奖励时,服务A必须向服务B询问相关信息。而在事务性场景中,服务A需要在完成订单并已发放奖励之后获取已发送的奖励信息。这种转变从最终一致性到事务一致性是非常具有挑战性的,我们需要时刻关注实体之间的关系以及它们是如何处理的。
管理一系列事件的困难
在事件驱动的架构中,事件订阅者可以确信事件发布者已经完成了其任务。管理事件链带来了一套新的挑战。在某些情况下,如服务D作为整个事件链的最终环节时,它需要确定与事件C及其相应监听器相关的流程是否已完成,这会变得非常困难。如果服务D需要发送邮件通知用户所有流程(包括A、B和C)已完成,而它无法确定与事件C相关的流程是否完成,就会成为一个问题。一个策略设计,旨在对事件赋予优先级,以确保优先级较低的事件延迟发布。这种方法虽有助于安排事件顺序,但也在事件间构建了隐含的依赖关系。每当系统引入新事件时,都需要慎重考虑其位置与影响。
在这个错综复杂的事件链中,一个核心问题是:新事件应置于何处?哪些事件已被赋予优先级?回答这些问题需要对整个事件链进行深度追踪,其复杂性远超过在单一场景下排列服务顺序。
通过这种方式,我们既可以充分利用事件驱动模式的灵活性,又可以避免由于过度复杂化和隐含依赖关系带来的困扰。这一转变是基于对事件驱动模式深入理解和实践经验的基础之上的,旨在为我们提供一种更为实用和高效的解决方案。
文章来自《钓虾网小编|www.jnqjk.cn》整理于网络,文章内容不代表本站立场,转载请注明出处。