EDA中事件内容的设计权衡


用例:下订单。
成功下订单后(付款成功并且向餐厅成功下订单后),订单服务会发布一个事件:

  1. 食品配送合作伙伴服务,用于安排送货人员到达餐厅、挑选食物并将订单配送到用户的地址
  2. 将积分记入用户帐户的忠诚度服务
  3. 通知服务通过电子邮件/短信/移动推送向用户发送订单确认通信。
  4. 分析服务,用于准备应用程序的所有与分析相关的仪表板 - 每天/小时、每个位置收到的订单总数等。

在我们最初的实施中,我们采用了非常精益的方法 -

  1. 订单服务发布一个事件,其中仅包含用户成功下达的订单的订单 ID。
  2. 事件的消费者服务对订单服务进行 API 调用,以获取所下订单的更多详细信息,并进一步对其他微服务(如用户服务和餐厅服务)进行其他 API 调用,以获取执行其业务逻辑所需的其他数据。

当被问及为什么采用这种方法时?他回应说——

  1. 嗯,重要的一点是 - 我不必担心事件有效负载的版本控制和合同管理。
  2. 订单服务充当所有所下订单的单一真实记录,并且所有消费者服务在使用订单 ID 请求数据时始终从订单服务获取最新数据。因此,如果订单在下订单后立即被取消,则处理“下订单”事件时的消费者服务将获取表示订单已取消的最新数据,因此不会进行进一步的处理。

这种方法遇到什么问题吗???
系统的可扩展性和性能受到严重打击!

由于事件仅携带订单 ID,因此为了处理订单,所有消费者服务(例如忠诚度服务、交付合作伙伴服务、通知服务、分析服务)开始多次调用订单服务和其他域服务,以获取执行任务所需的数据。业务事务,这导致域服务负载增加,并且作为级联结果,系统的整体性能开始下降。

怎么解决这个问题的?

我们重新查看了发送的事件,并修改了有效负载以携带更多数据 - 它现在包含所有其他消费者服务所需的所有信息,以帮助他们执行业务需求,而无需进行额外的查找。

  1. 对于成功下达的每个订单,订单服务都会调用一次用户服务和餐厅服务以获取其他数据。因此,来自每个消费者服务的多个 API 调用将被针对用户和餐厅服务的单个 API 调用所取代。
  2. 使用每个消费者服务所需的所有数据创建一个事件并发布该事件。

这种修改后的方法的优点是:

  1. 消费者服务和其他领域服务之间的通信量大大减少,消费者服务不再需要多个重复的数据请求来处理单个订单,从而减少了服务数据库的负载,从而提高了整个系统的可扩展性和性能。

这种方法的缺点是:

  1. 现在我们需要管理合同管理和事件负载的版本控制。订单服务现在需要了解每个消费者服务的确切要求,并且必须确保事件中存在所有这些必需的属性。如果添加了需要使用已下订单事件的新服务,或者现有服务需要更多数据,则消费者服务所有者必须联系订单服务团队以获取所需的附加数据和新版本的事件将会被浮动。现在,您需要不断考虑弃用旧版本、保留最后 n 个版本等策略,这会影响订单服务的可维护性软件特性。
  2. 您进入了消息耦合——并非所有消费者服务都会消耗事件的所有属性。忠诚度服务可能只需要用户 ID 和订单金额等属性,而送货合作伙伴服务则需要取餐的餐厅地址以及必须送货的用户地址。交付合作伙伴服务所使用的现有属性的更改(例如属性名称的更改)也可能会影响忠诚度服务读取事件的方式。
  3. 由于事件携带整个有效负载,因此它充当所有消费者服务的记录系统。如果用户立即取消已下订单,则消费者服务仍将处理已下订单事件,然后处理已取消订单事件。

在下面几点权衡:

  • 系统的可扩展性和性能
  • 活动的合同管理和版本管理
  • Stamp 耦合
  • 单一记录系统

我们选择了 "可扩展性 "和 "性能",并增强了我们的系统,以解决上述其他痛点。