我们经常看到随着Event Sourcing一起出现的,还有几个大家比较熟知的概念:CQRS, EDA(Event-driven Architecture),当然还有DDD。在经历过采用Event Sourcing的项目后,我想和大家讨论一下,当我们提到Event Sourcing时,我们在说什么?再简单阐述一下这四个概念之间的关系。
提到Event Sourcing,我们会联想到一个非常相近的生活中的例子就是会计账本,会计账簿上的会计条目按照发生的时间顺序,记录了对账户余额产生变更的事件。通过会计账簿的记录,我们可以计算出任意时间点的账户余额。如果说有一类应用程序需要留存对最终结果造成改变的所有事件,那Event Sourcing就像是这类应用程序的概念抽象。所以我们看到Event Sourcing也有着和会计账簿一样的特征:
听起来Event Sourcing的概念也没有那么难理解,更加贴合现实,还保存了所有真实事件,如果有审计相关的需求,显然是很容易得到审计需要的数据。
Event Sourcing在Node.js里并不是一个被广泛使用的成熟设计,我们很难在市面上找到成熟的Node.js的Event Sourcing框架,这意味这我们可能会面临更多的未预知的问题。除了未预知的问题之外,开发团队还面临着巨大的思维转变:系统中的一等公民变成了事件,所有的逻辑都是围绕着事件展开,系统状态不再是一个一定需要被持久化的元素了。这听起来很简单但是实践起来却并非只言两语可概括般的容易。
我了解到有的项目有基于命令转化为事件,并将事件持久化到数据库,但是在此同时他们也把command转化为snapshot保存了下来。读模型的构建全部基于snapshot。该团队确实将系统发生的真实事件全部留存了下来,但实际并没有通过事件重建得到状态,所有的状态都是来自于另外处理得到的snapshot。严格上来说并不能算做是使用了Event Sourcing,系统中做到了留存Event,但是并没有用到Sourcing。
基于一些背景信息,当时该项目使用Event Sourcing的出发点在于,客户强烈要求将DDD的思想和产出的模型完全代码化,特别是在Event Storming过程中的产出。
上面的例子不禁让我们思考一个问题:究竟在什么情况下需要用到Event Soucing?
为了回答这个问题我们先来看看Event Sourcing中的核心概念:
Event:发生的事实,也是唯一真实的数据来源。用过去式来表述。系统中的事件是immutable的,不能被更改和删除,只能通过添加新的事件来改变当前的系统状态。
完全重建(Rebuild):我们可以完全丢弃系统状态,在任何时间通过事件日志按照时间顺序重演出当前系统状态。
事件回放(Replay): 就像平时浏览视频一样,如果视频总时长是半小时,我们想回到25分,我们可以直接把进度条向后拉到25分。在Event Soucring的系统里,我们可以基于某个重建出来的系统状态,回放后续的事件,得到我们想要的某个时间节点的系统状态。
又比如: 我们的会计账簿里保存了过去一年全部的会计条目,现在想要得到5月30号当天的账户余额,在此前因为业务要求我们已经得到了每个季度结束的账户余额。那我们可以通过已知的5月31号的账户余额对5月31号发生的所有存款和取款进行反向的重放得到5月30号的余额。
基于Event Sourcing的特性,我们可以来探讨下它究竟能给我们的系统或者说业务带来怎样的好处?
从它能带来的优点来看,当我们的业务需求有:
当然决定用它之前我们还是得考虑一下它的缺点:
回到文章开头提到的四个经常被拿来一起说的概念:当我们决定使用Event Sourcing作为架构选择之时,通常我们也会选择DDD去构建得到领域事件。DDD里提到的Event指的是对系统状态产生改变的现实事件,同样我们在Event Sourcing的系统中存储的也是会导致系统状态改变的事件。似乎这两种不同的软件开发思想,对Event的认识有着不谋而合的默契。
用到Event Sourcing的系统又绝大部分都会采用CQRS。因为我们持久化的event和查询所需的结构很显然是有区别的,与其每次查询都通过Rebuild或者Replay去得到查询所需的状态,我们一般都会根据查询需求构建出查询需要且方便的读模型。即便如此,当我们决定选用CQRS时,还是得考虑引入后会增加的的复杂度。这也意味着不是所有的Event Sourcing的系统都需要采用CQRS。
至于EDA那其实是完全没有太大关系的概念了,不过我们经常在处理服务之间通信的问题的时候会用到。当我们的项目恰好是微服务,又采用了DDD,还加上Event Sourcing和CQRS那我们还需要引入EDA的时候,就要小心我们平时的技术讨论中一定要分清楚我们所说的Event是在怎样的上下文下的。想要更多的了解EDA的概念可以参看Martin Fowler“当提到“事件驱动”时,我们在说什么?”的文章,其中也提到了我们经常会混用Event Sourcing,EDA,CQRS中的一些概念。
希望这篇文章能够引发你对Event Sourcing的设计思想的一些思考。也期望后续我还能再完成一篇Event Sourcing实战的文章。这篇文章其实还是有些遗漏的地方,比如在Event Sourcing架构选择决策的缺点部分,但是考虑到实际选用Event Sourcing架构的情况下通常还会选用其他的设计以及架构,比如文中反复提到的CQRS和DDD,在最终决定的架构下也会引入除了本文所提的缺点之外的其他问题,但因为我认为这并不算是Event Sourcing架构本身带来的问题故没有在文中深究。但是如果大家真的决定选用Event Sourcing作为系统设计思想的一部分的话还是需要对Event Sourcing的应用做更多的探索,本文还是旨在阐明Event Sourcing的概念,消除大家对于Event Sourcing的部分误解。
文/Thoughtworks 苏晓风
原文链接:如何正确使用Event Sourcing-Thoughtworks洞见