对LMAX架构以及Event Sourcing模式的一些新思考和问题的记录

简介:

最近又学习了一下LMAX架构,让我对该架构以及event sourcing模式又有了很多新的认识和疑问。

注:如果不知道什么是lmax架构和event sourcing模式的看官可以自己先去查查资料:

  • LMAX可以看看martin写的一篇文章:http://martinfowler.com/articles/lmax.html
  • Event Sourcing的资料比较多,随便google一下即可。
  • 当然,我的博客里也有大量关于这两个方面的笔记,有兴趣的可以看看。

下面是我的一些最新的想法。

LMAX architecture:input event + business logic processor(BLP) + output event

架构主要执行过程:
首先input event由上层(如controller)创建并最后统一汇集到input disruptor(一个并发控制组件),然后BLP在单个线程内处理所有的input event,一般处理的情况有:1)简单时,直接让aggregate 处理,处理完之后aggregate会产生output event;2)如果是复杂的过程,如long running process,则通过saga的方式来控制整个业务流程;首先也是由aggregate来处理input event,然后产生的output event会由saga响应,然后saga会根据流程逻辑决定接下来要做什么,即产生哪个input event;实际上我把saga也看成是一种聚合,因为saga也有状态,saga表达了一个流程的处理状态,saga也有唯一标识,saga也需要被持久化;总之,BLP在处理完input event后会产生output event。然后这些output event会被某些关心的event handler处理;另外有些event handler在处理output event时又会产生另外的input event并最终也发送到input disruptor,整个过程大概是这样。不知我理解的是否正确。

下面针对我上面的理解再做一些总结:

  1. 整个过程有下面这几个主要元素构成:input event + BLP(包含aggregate,saga) + output event;
  2. input event,output event用于消息(message)传递,实际上他们都属于消息,并且也都是domain event?
  3. BLP用于处理业务逻辑(由aggregate负责)和流程控制逻辑(由saga负责);
  4. aggregate产生output event,output event会最终被发送到output disruptor;
  5. output event有两个主要作用:1)可以让领域外知道领域内发生了什么;2)可以通过output event串联某些复杂的业务过程,如银行转账,如提交订单,etc;
  6. 值得注意的是:整个BLP(saga+aggregate)是in-memory的,重建BLP是用input event来实现,而不是output event;这也是为什么LMAX架构中在BLP处理input event之前必须先通过一个叫journaler的东西持久化input event的原因。目的就是为了在需要的时候利用这些input event通过event sourcing(事件溯源)模式重建整个BLP。其实这个行为更直白的理解就是让BLP再重新处理一遍所有的input event;当然,在重建过程中对于任何要访问外部系统接口的地方,都要禁止访问,否则会带来问题,尤其是更新外部系统的时候,这个其实比较简单,只要设计一个gateway即可,重建blp的时候设置一下该gateway即可。

接下来我想阐述一些我觉得自己比较纠结的地方:

event sourcing的中文解释是事件溯源,关键是如何理解溯源?我的理解是:根据已经发生的事情来重现历史。如果这个理解是正确的,那何为已经发生的事情?lmax是通过input event来溯源,也就是说Lmax认为已经发生的事情是input event,而非output event,即LMAX认为已经发生是指只要input event一旦被创建就表示事情已经发生了,即已经发生是针对用户而言的,如用户提交了订单,那就是OrderSubmitted,用户点击了修改资料的保存按钮,那就是UserProfileChangeRequested;而我们之前的做法是根据aggregate产生的output event来溯源,即我们认为已经发生是相对aggregate而言的;那么到底哪种思路更好呢?虽然两种做法都能最终还原BLP。但就我个人理解,我觉得lmax的做法更合理,实际上如果让LMAX和CQRS架构的command端做对比,那么input event相当于command,只不过command一般都是动词,所以就是CreateOrder,ChangeUserProfile。所以可以理解为lmax架构实际上是在replay command;所以问题就演变为我们到底应该replay command还是replay event?想想replay是谁在replay?是聚合根,这点毫无疑问。另外,replay从语义上来说实际上就是和play做的事情是一样的,只不过是“重做”的意思。那么要理解重做首先要理解什么是“做”?我对“做”的理解就是执行行为并改变状态。所以“重做”就是重新重新执行行为并改变状态;replay command相当于是在重做别人给aggregate一些命令;而replay event相当于是在重做aggregate自己以前曾今做过的一些事情。其实,最重要的一点是,到底要重做什么?是重做用户的要求(what user want to do)还是重做聚合根内已经发生的事情(what domain has happened.),这个问题的回答直接决定到底该replay command 还是 replay event,呵呵。所以,按照这样的思路来思考就很明显了,LMAX是在重做用户的要求,而我们之前的replay event则是在重做聚合根内已经发生的事情。如果我认为重做应该是重做用户的要求,那replay event就不是真正意义上的重做了,而仅仅只是改变状态。举例:假设有一个Note聚合根,有一个ChangeTitle的公共方法,然后还有一个ChangeTitleCommand,ChangeTitleCommand的handler会调用Note的ChangeTitle方法;另外Note还有一个OnTitleChanged的私有方法,用于响应TitleChanged事件。如果是replay command,那会导致ChangeTitle会被重新调用,这就是重做用户的要求;而如果是replay event,则只有OnTitleChanged方法被重新调用,也就是说只是在重做聚合根内已经发生的事情。思考到这里,我不得不承认第一个思考出这种思路的人很厉害,因为他用了这种绕个弯的做法(将本来可以放在一个方法内一次性完成的任务(先改状态然后再产生output event))拆分为两个步骤,第一步是先仅仅产生一个TitleChanged的事件,第二步才是响应该事件并作出状态改变。这样拆分的目的是可以让第二步的方法(OnTitleChanged方法)可以用于event sourcing。另外,这两步对聚合根外部来说是透明的,因为外部根本不知道内部是通过两个步骤来实现的。不得不承认这种做法在replay的时候远比replay command要容易的多,因为所有的aggregate的内部事件响应函数都不会涉及与任何外部系统的交互。虽然这种做法挺好,但是我觉得我们非常有必要搞清楚这两种不同的event sourcing的区别。

另一方面,从确保event必须被持久化的角度来讲:我觉得LMAX的架构,即replay command的好处是,可以很容易在进入BLP之前持久化command,真正做到在BLP处理之前确保所有事件已经被持久化了;而如果是replay event,那我们就没办法实现一个in-memory的BLP了,因为首先BLP是in-memory的,即没有任何IO,但是我们又要求必须持久化output event。那怎么办呢?如果是同步的方式持久化output event,那就不是in-memory了;如果是异步的方式来持久化output event,那虽然可以做到in-memory,但怎么确保output event一定已经被持久化了呢?

目前就这些了,以后有更多的思考内容再补充上来。

-------------------------------------------------------------------------------------------------------------------------------

后来又做了一些思考。

想来想去,最终还是倾向于应该通过output event来做event sourcing。因为毕竟只有output event才真正表示domain aggregate认可的可以发生的domain event。而我们要重建的就是聚合根,到底是应该通过重复执行用户的命令来让模型达到最新状态还是通过让聚合根重新执行已经发生过的事情呢?现在想来,应该是后者。虽然前者也可以,但是要付出的代价相对比较大,比如重建时要禁用外部系统的调用,最麻烦的还是重启发布时的很多细节问题要考虑;而通过聚合根已经发生的事情来重建,则相对很容易,因为重建时不会涉及任何模型之外的东西!

但是因为我们现在采用了in-memory domain的架构,所以传统的基于数据库事务的做法已经无法使用了。所以需要设计另外一种架构确保在domain修改状态之前domain event已经被持久化了,为什么要做这个保证是因为event sourcing+in memory的架构实际上是一种event driven architecture,即整个领域模型的状态的修改都是由事件驱动的,这意味着如果要改变内存中的领域模型的状态,那必须先确保引起该状态修改的domain event必须已经被持久化了。

从用户发起一个command后执行的流程如下:disruptor是并发控制组件,大家可以暂时理解为一个消息队列。如果要进一步了解disruptor,可以看看LMAX架构。

  1. Send ChangeNoteTitleCommand to input disruptor;
  2. Command handler execute method called by input disruptor;
  3. Note.ChangeTitle method called by command handler execute method;
  4. NoteTitleChanged domain event is created and raised in note.ChangeTitle method;
  5. The infrastructure framework send the above NoteTitleChanged event to input disruptor when the raise method is called;
  6. Journal event handler called by input disruptor to persist the event;
  7. Another event handler called by input disruptor to really apply all the note state changes according with the event.
  8. A third event handler called by input disruptor to send the event to the output disruptor;
  9. All the external event handlers are called by the output disruptor; for example, some external event handlers will update the CQRS query side data;

以上步骤必须严格按照上面的顺序一步步执行下来,否则无法确保逻辑正确。
另外,以上流程目前只考虑单台机器,未考虑主备或集群的架构如何实现;之所以用英文写是因为我还要拿去和老外讨论,呵呵。不过这几句英文应该比较简单吧。

目录
相关文章
|
3月前
|
负载均衡 应用服务中间件 API
探索微服务架构中的API网关模式
在现代软件开发中,微服务架构已经成为一种流行的设计模式。它通过将复杂的应用程序分解为一组小的、松耦合的服务来简化开发和部署。然而,随着服务数量的增加,如何有效地管理这些服务之间的通信成为了一个挑战。API网关作为微服务架构的关键组件,提供了一个集中式的入口,用于处理客户端请求并将其路由到相应的服务。本文将深入探讨API网关的作用、实现方式以及如何在微服务架构中有效地利用它来优化系统性能和安全性。
42 0
|
11天前
|
JSON 监控 安全
探索微服务架构中的API网关模式
【9月更文挑战第22天】在微服务架构的海洋中,API网关如同一位智慧的守门人,不仅管理着服务的进出,还维护着整个系统的秩序。本文将带你一探究竟,看看这位守门人是如何工作的,以及它为何成为现代云原生应用不可或缺的一部分。从流量控制到安全防护,再到服务聚合,我们将一起解锁API网关的秘密。
|
2月前
|
分布式计算 负载均衡 API
微服务架构设计原则与模式
【8月更文第29天】随着云计算和分布式计算的发展,微服务架构已成为构建大型复杂应用的一种流行方式。这种架构模式将单个应用程序分解成一组小型、独立的服务,每个服务运行在其自己的进程中,并通过轻量级机制(通常是HTTP资源API)进行通信。本文将探讨微服务架构的基本设计原则、常用模式以及如何有效地划分服务边界。
147 3
|
2月前
|
弹性计算 Kubernetes Serverless
Kubernetes 的架构问题之Serverless Container中不支持特权模式的问题如何解决
Kubernetes 的架构问题之Serverless Container中不支持特权模式的问题如何解决
70 6
|
2月前
|
设计模式 监控 API
探索微服务架构中的API网关模式
在微服务的宇宙里,API网关是连接星辰的桥梁。它不仅管理着服务间的通信流量,还肩负着保护、增强和监控微服务集群的重任。本文将带你走进API网关的世界,了解其如何成为微服务架构中不可或缺的一环,以及它在实际应用中扮演的角色和面临的挑战。
|
2月前
|
C# 微服务 Windows
模块化革命:揭秘WPF与微服务架构的完美融合——从单一职责原则到事件聚合器模式,构建高度解耦与可扩展的应用程序
【8月更文挑战第31天】本文探讨了如何在Windows Presentation Foundation(WPF)应用中借鉴微服务架构思想,实现模块化设计。通过将WPF应用分解为独立的功能模块,并利用事件聚合器实现模块间解耦通信,可以有效提升开发效率和系统可维护性。文中还提供了具体示例代码,展示了如何使用事件聚合器进行模块间通信,以及如何利用依赖注入进一步提高模块解耦程度。此方法不仅有助于简化复杂度,还能使应用更加灵活易扩展。
61 0
|
2月前
|
前端开发 开发者 C#
WPF开发者必读:MVVM模式实战,轻松实现现代桌面应用架构,让你的代码更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,MVVM(Model-View-ViewModel)模式通过分离应用程序的逻辑和界面,提高了代码的可维护性和可扩展性。本文介绍了MVVM模式的三个核心组件:Model(数据模型)、View(用户界面)和ViewModel(处理数据绑定和逻辑),并通过示例代码展示了如何在WPF项目中实现MVVM模式。通过这种方式,开发者可以构建更加高效和可扩展的桌面应用程序。
57 0
|
2月前
|
设计模式 测试技术 数据处理
|
2月前
|
负载均衡 监控 JavaScript
探索微服务架构下的API网关模式
【8月更文挑战第31天】在微服务的大潮中,API网关不仅是流量的守门人,更是服务间通信的桥梁。本文将带你深入理解API网关的核心概念、设计要点及其在微服务架构中的重要作用,同时通过代码示例揭示如何利用API网关提升系统的灵活性与扩展性。
|
2月前
|
负载均衡 监控 API
探索微服务架构中的API网关模式
在微服务架构的海洋中,API网关扮演着枢纽的角色。它不仅是客户端请求的接收者,也是各个微服务间通信的协调者。本文将深入探讨API网关的设计原则、实现策略以及它在微服务生态中的重要性。我们将通过实际案例分析,了解API网关如何优化系统性能、提高安全性和简化客户端与服务的交互。
43 4
下一篇
无影云桌面