【问题标题】:Designing event-based architecture for the customer service为客户服务设计基于事件的架构
【发布时间】:2021-11-20 10:04:17
【问题描述】:

作为一名经验丰富的开发人员,我只是进入微服务和事件驱动架构的世界。与传统的单体方法相比,我认为松散耦合、独立可扩展性和异步业务流程的正确实现之类的事情应该得到简化。所以尝试一下,为自己制作一个简单的 PoC。 我正在考虑制作一个简单的应用程序,用户可以在其中注册、登录和更改客户详细信息。但是,我想对某些事件异步做出反应:

  1. 客户登录 - 如果使用的 IP 地址对系统来说是新的,我们会向他们发送电子邮件。
  2. 客户更改姓名,我们会向他们发送电子邮件通知更改。

我们的想法是制作一个对“CustomerLoggedIn”、“CustomerChangeName”事件做出反应的单独应用程序。 在这里,我可以想到三种方法,如何实现这个简单的功能,每种方法都有一些缺点。因此,当客户提交姓名更改时:

  1. 存储更改名称 更改的名称存储在数据库中 + 当数据库事务完成时,将向 Kafkas 发送一个事件。这里出现的一个大问题是,如果客户打开了 2 个选项卡,并且几乎同时在一个选项卡中提交了从初始名称“Bob”到“Alice”的更改,在另一个选项卡中从“Bob”到“Jim”,在数据库级别的更新之一覆盖另一个,这没关系,但是我们不能保证事件的顺序是相同的。我们可以使用一些检查来确保仅在看到“最后一个版本”时才进行数据库更新,从而完全防止第二次更新,因此只会发出一个事件。但在一般情况下,这种模式不允许我们在数据库中保留与 Kafka 中相同的事件顺序,除非我们在一个分布式事务中执行数据库更改 + Kafka 事件发送,这是反模式 afaik。

  2. 在 DB 中更改名称,并使用 Debezium 或类似的 DB CDC 来捕获事件并将其流式传输。在这里,我们得到一个单一的事件源,因此排序问题得到了解决,但是困扰我的是我失去了用业务信息丰富事件的能力。另一个相关的缺点是 CDC 将流式传输“客户”表中的所有更新,而不管事件的业务含义如何。因此,在这种情况下,我可能需要构建一个 Kafka Streams 应用程序来将 DB CDC 事件转换为业务事件,并将 DB 结构与事件结构分离。这种方法的潜在好处是我将能够以与源自应用程序相同的方式捕获“直接”数据库更改。

  3. 从应用程序发出事件,而不将其存储在数据库中。一个订阅者可能会使用 DB 持久性,另一个订阅者会发送电子邮件等。我在这里看到的最大问题是 - 我应该向客户端返回什么?我不能说“好吧,你的名字改了”,更像是“好吧,你的请求已经被记录了,将被处理”。如果客户快速点击刷新 - 他希望看到他的新名称,因为我们不想向客户解释什么是最终一致性,是吗?此外,“email sender”和“db updater”处理同一事件的顺序也不能保证,所以我可以在更改保留之前发送电子邮件。

我正在寻找关于这三种方法中的任何一种的建议(可能还有一些我缺少的方法),也许是一种比其他方法更受欢迎的用例?

【问题讨论】:

    标签: apache-kafka microservices debezium event-driven event-driven-design


    【解决方案1】:

    在我看来你想要事件溯源。在事件溯源中,您需要存储的只是事件:客户的当前状态来自重放事件(从时间开始或从快照开始:快照只是一个可选的优化)。然后,其他一些过程(有几种方法可以解决这个问题)可以将事件投射到 Kafka 以供相关方使用。由于每个事件都有一个序列号,因此您可以使用序列号来防止并发修改(或者,更多的 Actor 模型事件源实现可以使用 Akka 中的集群分片等技术来实现相同的目的)。

    这样做,您可以拥有一个“写入端”,它以高度一致的方式处理更新,并且可以响应仅涉及到该点的每个更新的单个客户的查询(一致性边界基本上使客户这种情况是领域驱动设计术语中的聚合)。 “读取端”消费事件最终是一致的:延迟通常相当短:在这种情况下,您发送电子邮件的服务是读取端(就像显示所有客户姓名的假设面板一样),但客户对他们自己的看法数据可以由写入端提供。

    (读取端和写入端的分离(复数很重要)是命令查询责任分离,有时被解释为“读取只能由读取端提供服务”。这并不完全准确:一方面,需要读取写入端的模型,以便写入端执行其验证命令和同步更新的任务,因此几乎所有使用 CQRS 的项目都违反了这种解释。CQRS 应该被解释为“服务读取”从最有意义的模型中提取并避免过度复杂化模型(包括写​​入端的模型)以支持新的读取”。)

    【讨论】:

      【解决方案2】:

      我认为我有资格回答这个问题,因为我已经广泛使用 debezium 来简化架构。

      我更喜欢选项 2:

      • 每笔交易都会导致事件以正确的顺序发出
      • 选项 1/3 有一个极端情况,如果事务成功,但应用程序未能发出事件怎么办?

      你的意思:

      另一个相关的缺点是 CDC 会将所有更新流式传输到 “客户”表不考虑事件的业务含义。 所以,在这种情况下,我可能需要构建一个 Kafka Streams 将数据库 CDC 事件转换为业务事件的应用程序和 将 DB 结构与事件结构解耦。

      我真的不认为这是一个障碍。您获得的好处是可能会出现其他用例,其中该主题的另一个消费者可能想要阅读表格的其他列。

      选项 1 和 3 只会将其与您的核心应用程序逻辑联系起来,这对简化 PoV 没有任何帮助。使用选项 2,对核心应用程序 API 进行零代码更改,开发人员可以独立处理事件,而无需了解核心逻辑。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-09-04
        • 1970-01-01
        • 2012-03-27
        • 2013-05-22
        • 2018-05-13
        • 2011-07-02
        • 2012-04-27
        • 2017-12-06
        相关资源
        最近更新 更多