头图

千奇百怪的事件驱动模式

XuDing

本文转载自【何以解耦】:https://codedecoupled.com/eve...

Martin Fowler 在2017年做了一次关于事件驱动模式的演讲【The Many Meanings of Event-Driven Architecture】。此文是本人观看演讲后的一篇总结。由于视频在油管,无法翻墙的同学可阅读文字版

当不同的开发者提及事件驱动时,他们可能意味着完全不一样的概念。Martin Fowler 将不同的事件驱动模式进行梳理,细分为以下四种。

事件通知(Event Notification)

事件通知模式是指发送方通过事件的方式通知接收方自身领域的状态更改。大部分情况下发送方不需要等待任何回应。

用一段简单的 PHP 代码展示事件通知模式:

class OrderShipped
{
    private int $orderId;

    public function __construct(int $orderId)
    {
        $this-> orderId = $orderId;
    }
}
class SendShipmentEmail
{
    private int $orderId;

    public function handle(OrderShipped $event)
    {
        // 查询 Order 详情,然后发送邮件
    }
}
// 发布 OrderShipped 事件
OrderShipped::dispatch($order);

事件通知模式可降低代码的耦合性,SendShipmentEmail 通过监听 OrderShipped 事件独立工作,耦合性降低,单元测试方便且直接。

同时也注意事件本身并不携带很多信息,接收方需通过查询源数据来获取所需数据。

避免陷阱

虽然使用事件通知模式可降低代码耦合性,而且高内聚低耦合是优秀软件设计的标准,可是过度使用或者不正确使用事件通知模式,可能会导致代码逻辑混乱,难以理解,给 debug 制造很多麻烦。

由于事件通知模式一般是通过观察者模式实现,在代码层面无法直观的看出一个事件发布以后所造成的影响。比如在上文中,OrderShipped 发布以后,代码流程是看不出 SendShipmentEmail 将被触发的。

携带状态事件转移(Event-Carried State Transfer)

在事件通知模式中,由于事件本身的信息比较少,所以接收方往往需要再次通过访问发送方来获取业务所需的信息。在相对独立的系统中,这种方式效率低且等待时间过长。这时我们可以将接收方需要的所有信息都加载至事件中,发送方和接收方各自维持自己的状态。这种事件驱动模式,我们称其为携带状态事件转移模式。

优点还是缺点

这种模式的效果是接收方将出现大量的数据副本,而且接收方需要维持其版本的秩序。维护数据副本的成本用来换取延迟降低的好处,优点还是缺点,因应用需求而异。

事件溯源(Event Sourcing)

将每个状态变化存储为一个单独事件,应用状态通过播放事件来获取,这样一种数据持久化方案叫做事件溯源。与面向状态的持久化方案相比,事件溯源提供了一些显而易见的好处:

  • 自带审计跟踪功能。每一个状态变化都追加至存储器中,存储器中自然而然形成了一个审计日志。
  • 易于排查 bug。这是个人认为事件溯源提供的最大好处,有序阅读事件可以直观地分析出状态是如何的形成。
  • 事件旅行。历史记录以及历史状态都可以方便地通过重播事件来获取。

image.png

挑战

事件溯源也有自身的挑战,比如当应用与第三方发生交互,当重播时,如何处理数据的一致性,如何保证第三方的稳定性,这是在使用事件溯源时,需要面临的挑战。

命令查询职责分离(CQRS)

严格来说命令查询职责分离模式不属于事件驱动模式,由于它经常与事件溯源混淆,所以 Martin Flow 将它归类其中。简单来说,命令查询职责分离是指将数据的写入以及读取彻底分开,这和数据库的读写分离非常类似,但它是一种应用架构模式,大部分情况下,它的写入(命令)部分使用事件溯源来实现。

image.png

总结

使用事件驱动编程时,只有理解其本质才能避免囫囵吞枣,弄巧成拙。

本文转载自【何以解耦】:https://codedecoupled.com/eve...,如果你也对 TDD,DDD以及简洁代码感兴趣,欢迎关注公众号【何以解耦】,一起探索软件开发之道。

阅读 194

全职编程,兼职跑步,临时健身。

7 声望
0 粉丝
0 条评论

全职编程,兼职跑步,临时健身。

7 声望
0 粉丝
文章目录
宣传栏