如何理解事件溯源

在近期举行的PHPDublin见面会上,来自DynamicRes的架构师Barry
Sullivan被问到“什么是事件溯源”,作为对这个问题的回答,他在博客上写下了这篇文章,详细解释了什么是事件溯源以及事件溯源有哪些好处。以下内容翻译自Barry的博客,已获得作者授权。

Web开发的现状

在详细解释事件溯源之前,先让我们来看看Web开发的现状。

当前的Web开发是以数据库作为驱动的,在设计Web应用的时候,我们会自然而然地将系统设计与数据库存储机制联系在一起。如果使用的是MySQL,我们就会把数据结构设计成表,如果使用的是MongoDB,就会把数据结构设计成文档。这样会强制我们只关注事物的当前状态,我们会想“怎样保存这些数据,以便将来可以再把它们拿出来(或者修改它们)”?

  这种方式存在三个问题。

1. 有悖于我们的思维

人类的思考和交流并不是以状态为中心。如果我们在咖啡店相遇,我会问你“最近可好”?如果你只是告诉我一堆状态却指望我从中猜出发生了什么事情,这是非常不合情理的。

“我有一幢房子、一辆汽车、一个冰箱、三个社交媒体账号、一只猫咪,我的右脚有点痛,我不擅长聊天,我还有另一只猫咪……”

看到没有?这样我会疯掉的。你应该告诉我,从上次见面之后都发生了哪些事情,这样我才能知道你现在的状况是什么样子的。简单地说,你应该告诉我一个故事,这个故事是由一连串事件组成的。

2. 单一的数据模型

在上图中,读写操作使用了相同的模型。我们从写数据的角度来设计表,然后基于这样的结构查询数据。这对于小型的应用来说是没有问题的,但用在大型的应用里就会有问题。随着系统的增长,查询会变得越来越复杂,总有一天,一个查询可能会包含10个连接操作,代码有100行那么多。系统很快就会变得脆弱无比,难以维护和变更。

3. 关键业务信息的丢失

这是一个大问题。在以表作为驱动的系统里,你只保存了系统的当前状态,你根本就无法知道系统是如何达到当前状态的。如果我问你“这个用户修改了几次邮件地址”,你有办法回答吗?或者我再问“有多少人把一件商品添加到购物车里,然后又移除掉,直到一个月之后才买了那件商品”,你就更没法回答了。你存储数据的方式丢掉了很多有用的业务信息!

事件溯源

事件溯源与上述的情况恰好相反,它并不关心当前状态,而是关注持续不断的变化事件。

举个例子,假设我们有一个“购物车”,我们可以创建购物车,往里面添加商品或移除商品,然后结账。

购物车的生命周期可以包含如下一系列事件:

创建购物车往购物车里添加商品再次往购物车里添加商品从购物车里移除商品结账

这些就是一个购物车的生命周期,包含了一系列事件。这就是事件溯源,非常简单吧?

几乎所有的流程都可以被看成一系列事件。在与领域专家交谈时,他们不会提及“表”和“连接”,他们会将流程描述成一系列事件以及可以应用在这些事件上的规则。

如何实施业务规则?

大部分的业务操作都有硬性约束。对于购物车来说,它的约束就是“一件商品必须先被放进购物车后才能被移除”。如果一件商品没有被添加到购物车里,又怎么能够移除它?这种事件顺序是不可能发生的。在没有状态的情况下,你怎样才能知道“购物车里是否有这件商品”?

很简单,你只要检查之前是否发生过“商品被添加到购物车里”这个事件,这样你就可以知道购物车里是否存在这件商品,然后移除它。

这样不会浪费时间吗?

一点也不。一般来说,要执行约束,只需要获得事件的一个很小子集。通过简单的数据库查询就可以获得有用的历史事件,在加载完这些事件后重放它们,把它们“投射”出来,以此构建你的数据集。这样的操作其实是很快的,因为你使用的是本地的处理器,而不是执行一系列SQL查询(跨域网络的调用要比本地操作慢得多,至少会相差两个数量等级)。

如何展示数据?

如果说每一个状态都是通过重放事件来获得的,那么该如何抓取数据并把它们展示给用户看?每次都需要抓取所有的数据然后再构建这些数据集吗?

答案是你没必要这样做,这样做其实是很荒唐的。

你可以在后台构建数据集,然后把中间结果保存在数据库里。这样,用户就可以在很短的时间内查询到这些数据。

有了事件溯源,你就不再局限于当前的表结构。需要做其他的查询?只要设计一个新的结构就可以了。你可以自由地实现各种读取模型,在不需要它们的时候再把它们抛弃掉。

事件溯源的好处

1. 临时的数据结构

因为所有的状态都可以通过重放事件获得,所以就没有必要把当前“状态”与应用程序绑在一起。如果需要以新的方式查看数据,直接创建新的数据视图即可。不再需要繁杂的数据迁移脚本,要做的只是创建新视图,抛弃旧视图。我现在几乎离不开事件溯源了。

2. 与领域专家的沟通变得更简单

正如之前所述,领域专家通常将业务流程描述成一系列事件,而不是状态。基于事件溯源的系统与领域专家的描述不谋而合,所以就没有必要把他们的描述转换成技术概念,这样也避免了信息丢失。与领域专家的沟通因此变得更加顺畅,因为我们正在使用他们能够理解的语言与他们沟通,这也让软件开发变得很不一样。

3. 极具表现力的模型

在事件溯源模型里,事件是一等对象,事件模型更加接近于实际的业务流程。这让很多东西都变得清晰明了,你就不会陷入存储技术的泥潭。

4. 生成报告更轻松

在事件溯源系统里,生成复杂的报告是一件轻而易举的事情。你拥有完整的历史事件,它们按照时间排序,你可以尽情地使用这些历史数据。

以之前的例子为例,假设你想知道有多少个用户从他们的购物车里移除了商品,却在一周后购买了这些商品。按照一般的开发方式,通常需要几周的时间才能开发出这个功能,而在发布之后,需要等上一段时间,等计算完所有数据之后才能生成报告。而在事件溯源系统里,你可以马上得到报告。你还可以得到之前任何一个时间点的报告,仿佛拥有了一台时光机。

5. 服务集成唾手可得

在标准的Web开发流程里,集成两个系统通常会导致他们之间的耦合,而事件溯源系统通过事件来解耦被集成的系统。当一个系统发生某系事件需要触发另一个系统的某个流程时,只需要写一个事件监听器即可。这种机制可以让你在不修改已有领域代码的情况下增加新的集成逻辑或特性。

例如,你想要在用户注册的时候发送一封欢迎邮件给他们,你只需要创建一个事件监听器,监听“用户注册”事件,而不需要去修改注册逻辑代码。

6. 在一般的数据库上也能健步如飞

你不需要使用多么奇特的数据库来存储事件,一般的MySQL数据库就足以。数据库都针对追加操作进行过优化,所以存储数据的速度是很快的。这也就是为什么事件溯源在当前的技术条件下能够良好运作,保存事件都是追加操作。

7. 可以随意更换数据库

基于事件溯源的数据结构都是临时性的,所以你可以使用你喜欢的数据库来存储状态,也就是说你完全可以选择最好的工具来完成你的工作。如果你发现了更好的工具,可以在任何时候把旧工具替换掉。我们目前正在从MySQL迁移到OrientDB,可以说是轻而易举。

事件溯源的不足

天下没有完美的东西,事件溯源给我们带来了诸多好处,但也存在一些不足。

1. 最终一致性

事件溯源只能保证最终一致性。也就是说,在一个事件发生了之后,其他系统不会立即感知到它,在它们收到事件之前会有一定的延迟(比如100毫秒),所以你所投射的数据可能不是最新的。这看起来似乎是一个大问题,但其实不是的。例如,基于ReactJS构建的Web应用会基于用户的操作事件构建状态,查询端出现几毫秒的延迟并不会有什么问题。

老实讲,这可以说是塞翁失马,焉知非福。最终一致性的系统具有容错能力,可以解决服务中断问题。如果使用微服务架构或无服务器架构来构建分布式系统,就需要通过最终一致性来保证稳定性。

2. 事件结构发生变化

事件结构会发生变化,如果事先没有考虑到这个问题,后续处理起来会有些麻烦。如果事件结构发生变化,需要写一个更新器来转换新旧事件。转换过程可以在将数据从数据存储中读取出来之后进行。这个没有它看起来那么难,只需要准备好应对策略就可以了。

3. 开发人员需要改变思维

目前的Web开发主要还是以状态作为驱动,所以开发者习惯了从表的角度看待问题,而不是事件。我发现要让开发者改变思维需要一些时间,因为他们需要时间来改变习惯。最好的解决办法是让有经验的事件溯源开发者与传统的开发者结对。

总结

我很喜欢事件溯源,在构建大规模分布式系统时,它帮助我们解决了很多问题。我们可以使用领域专家能够了解的语言与他们进行沟通,我们可以自由地改变和适配系统。尽管事件溯源有一定的学习曲线,但一旦你进入到这个领域,就不会想要回头。

本文转自d1net(转载)

时间: 2024-09-09 10:21:57

如何理解事件溯源的相关文章

深入理解事件冒泡(Bubble)和事件捕捉(capture)_javascript技巧

事件的发生顺序 假设在一个元素中又嵌套了另一个元素并且两者都有一个onClick事件处理函数(event handler).如果用户单击元素2,则元素1和元素2的单击事件都会被触发.但是哪一个事件先被触发?哪一个事件处理函数会被首先执行?换句话说,事件的发生顺序到底如何?如下图是当点击span元素区域是,三个点击事件都会被触发,但是先后顺序是怎样的呢? <div onclick="func1"> <p onclick="func2"> <

事件溯源与流处理的对比

事件溯源(event sourcing)和CQRS是在领域驱动设计(Domain-Driven Design,DDD)社区出现的两个模式.流处理(Stream processing)构建在类似的理念上,但是它来源于不同的社区,Martin Kleppmann在今年的领域驱动设计欧洲会议的演讲中,将事件溯源与流处理进行了对比. Kleppmann之前有在互联网公司构建大规模数据系统的背景,但是他目前就职于剑桥大学,在将企业级软件与互联网公司的系统进行对比的时候,Kleppmann指出,它们主要的差

C#中委托,事件理解入门

目录 l        导论 l        什么是委托 l        事件的理解 l        事件 关键字 l        最后     导论     在学习C#中的委托和事件过程中,我读了许多文章来理解他们二者究竟是怎么一回事,以及如何使用他们,现在我将整个的理解过程陈述以下,我学到的每一方面,恐怕也是你们需要掌握的 :-). 什么是委托?     委托和事件这两个概念是完全配合的.委托仅仅是函数指针,那就是说,它能够引用函数,通过传递地址的机制完成.委托是一个类,当你对它实例

在微服务中使用领域事件

稍微回想一下计算机硬件的工作原理我们便不难发现,整个计算机的工作过程其实就是一个对事件的处理过程.当你点击鼠标.敲击键盘或者插上U盘时,计算机便以中断的形式处理各种外部事件.在软件开发领域,事件驱动架构(Event Driven Architecture,EDA)早已被开发者用于各种实践,典型的应用场景比如浏览器对用户输入的处理.消息机制以及SOA.最近几年重新进入开发者视野的响应式编程(Reactive Programming)更是将事件作为该编程模型中的一等公民.可见,"事件"这个

我对C#中事件委托的通俗看法

这是我对C#中事件的通俗看法,比较适合初学者(其实我也是初学者)对C#事件的理解,本人因为刚入门,在学习C#的事件原理时,发现很多书上写得很不好理解,不适合初学者,所以我想在这里谈谈我对它的看法,可能不是很准确,如果说得对的,请大家鼓鼓掌,不对的或是不规范的地方,还请高手帮忙更正一下,谢谢了. 在学C#之前,我看过<JAVA编程思想>,还有一些JAVA的入门级的书籍,其中对事件的讲解,总是用事件监听,事件适配器来举例, 所以我就一直认为,在C#中,RUNTIMING会一直监测每个控件的状态,如

Android View 事件分发机制详解_Android

Android开发,触控无处不在.对于一些 不咋看源码的同学来说,多少对这块都会有一些疑惑.View事件的分发机制,不仅在做业务需求中会碰到这些问题,在一些面试笔试题中也常有人问,可谓是老生常谈了.我以前也看过很多人写的这方面的文章,不是说的太啰嗦就是太模糊,还有一些在细节上写的也有争议,故再次重新整理一下这块内容,十分钟让你搞明白View事件的分发机制. 说白了这些触控的事件分发机制就是弄清楚三个方法,dispatchTouchEvent(),OnInterceptTouchEvent(),o

JavaScript事件委托的技术原理探讨示例_javascript技巧

如今的JavaScript技术界里最火热的一项技术应该是'事件委托(event delegation)'了.使用事件委托技术能让你避免对特定的每个节点添加事件监听器:相反,事件监听器是被添加到它们的父元素上.事件监听器会分析从子元素冒泡上来的事件,找到是哪个子元素的事件.基本概念非常简单,但仍有很多人不理解事件委托的工作原理.这里我将要解释事件委托是如何工作的,并提供几个纯JavaScript的基本事件委托的例子. 假定我们有一个UL元素,它有几个子元素: 复制代码 代码如下: <ul id=&qu

浏览器中关于事件的那点事儿(转)

摘要:事件在Web前端领域有很重要的地位,很多重要的知识点都与事件有关.本文旨在对常用的事件相关知识做一个汇总和记录. 在前端中,有一个很重要的概念就是事件.我对于事件的理解就是使用者对浏览器进行的一个动作,或者说一个操作. 本文会介绍很多与事件有关的东西,虽然我的出发点有那么点一网打尽的意思m不过也难以盖全,所以就把最常用,最基本也相对重要的内容拿出来记录一下. Javascript绑定事件的方式 传统的事件绑定 因为各种历史原因,事件的绑定在不同的浏览器总是有不同的写法,当然现在可能大多数人

JavaScript事件

JavaScript事件 对于事件来讲,首先,我们需要了解这样几个概念:事件:事件处理程序:事件类型:事件流:事件冒泡:事件捕获:事件对象:事件模拟,事件方面的性能优化(事件委托.移除事件处理程序): 事件的概念 事件:指的是文档或者浏览器窗口中发生的一些特定交互瞬间.我们可以通过监听器(或者处理程序)来预定事件,以便事件发生的时候执行相应的代码. 事件处理程序:我们用户在页面中进行的点击这个动作,鼠标移动的动作,网页页面加载完成的动作等,都可以称之为事件名称,即:click.mousemove