Event Sourcing 和 CQRS
目录
状态和事件
事件存储(Event Store)
事件朔源(Event Sourcing)
CQRS(Command Query Responsibility Segregation)
总结
状态和事件
我们可以把数据按照流的形式分为状态和事件,这个概念在大数据技术中被广泛地使用。状态是指一个数据的持久化表现形式,而事件是指达到某个状态的离散的过程。
比如张三在饭店点餐,点了一个汉堡、一包薯条、一杯可乐,在下单后,发现没有太大食欲,随后决定退掉一包薯条。基于上面的场景我们来尝试理解什么是状态和事件,以及它们之间的关系。
在上面的例子中,我们以订单的变更作为事件的触发,那么订单总共发生了两次改变。
- 下单:一个汉堡、一包薯条、一杯可乐
- 退单:一包薯条
我们把触发上面的两次改变的原因叫做事件,下单、退单是事件,而诸如“一包薯条”这样的数据是事件属性的一部分。
然后我们再来观察一下订单数据,订单数据在两次改变发生后分别变成了这样的数据
- 下单后:一个汉堡、一包薯条、一杯可乐
- 退单后:一个汉堡、一杯可乐
以上两个数据表现分别表达了下单时刻和退单时刻,订单数据的状态。事件和状态的关系如下图
本文主要讨论的内容聚焦于数据层面,所有这里的Action主要指将事件的数据属性聚合成为状态的过程,所有我们可以简单的将上面的过程描述成下图所示
所以单从数据层面考虑,状态是由离散的事件通过聚合形成的。状态是持久化的,它具有一定的时效性,比如上面的例子,退单前后的状态就是其时效性的表现。当然,对于一个餐厅收费系统来讲,退单后的订单状态才是该系统需要关注的内容,因为张三只会支付“一个汉堡、一杯可乐”的钱,所以对于收费系统来讲,中间产生的下单后,退单前订单的状态显得可有可无。这也是我们大部分系统的传统做法(本文后面的内容我们也会使用传统一词来描述这种做法),这样的我们关注于结果而缺乏对于过程的了解。
商业运营者往往会在收益下降时,选择引入分析系统,而分析系统最主要的数据来源其实就是事件,而我们设计系统的传统做法,无法满足于分析系统对于事件的需求。可能你会说你的系统设计上使用了审计日志、数据库Delta工具(CDC)等等来保证类似于事件的存储,但是值得一提的是那样的设计在系统性能上是有消耗的,而且平心而论,这些设计不是面向对象的、也不是用例级别的,它只存在于事务级别,这一块我们后续聊到DDD的时候可以继续探讨。
总而言之,我们传统的做法是状态存储,状态存储可能会丢失一些具有商业价值的事件数据。而本文则主要是讨论基于事件存储的系统模型。
事件存储(Event Store)
事件存储即将发生的所有事件直接入库存储,对比状态存储的更新(Update)操作,事件存储则是追加(Append)操作。大数据技术栈中基于流的描述也有对于的更新流和事件流的描述,实属异曲同工。
下面是基于一条数据的状态存储(传统存储)和事件存储的对比图
左边的圆圈是事件发生的时刻,和它平行的分别是状态存储(传统存储)和事件存储在该时刻需要入库的数据。我们可以看到,状态存储(传统存储)总是存储当前最新的数据状态,而事件存储则是以追加的形式将所有发生的事件入库。所以状态存储在任何时刻都只有一条数据,它使用更新操作来变更,而事件存储使用追加操作入库,不存在变更。而目前的存储(无论是数据库还是块存储)追加操作的性能远好于更新操作。
事件朔源(Event Sourcing)
上面讲到了事件存储,那么如果我们都只存储事件,那如何告诉我们的客户数据最新的状态呢?比如上面例子中的张三,应该付多少钱,付款金额取决于订单最终的状态。我们从本文的开头已经了解到了状态由事件聚合产生,而我们拥有所有的事件作为我们的数据源,剩下的就只是使用既定的算法将相关事件聚合起来形成状态。而从事件到状态的聚合过程就叫事件朔源,下图描述了所愿过程
从上面的图可以看出,我们不仅可以通过聚合获得数据的最新状态,还可以通过设定时间窗口来获得历史上某个状态,这样不仅利于数据分析还有利于bug重现。另外现实情况是用户使用我们的系统,在大部分情况下应该是只关注于最新状态,那么为了节约聚合过程中的算力,我们可能会根据某种策略(比如基于时间或事件个数)以打版本的方式将过去的事件聚合成那个时刻的状态并将其当中数据事件入库(上图中Snapshot-1就是T0-T7聚合后的状态,如果时间窗口大于T7,我们只需要将Snapshot-1和T8的事件聚合即可得到最新状态)
CQRS(Command Query Responsibility Segregation)
上面打版本的方式可以弥补我们基于事件溯源的一些算力的浪费,但是现实情况是,可能大部分的用户操作都是基于对最新状态的查询。我们的所有业务都是基于此前提来构建的,比如付款需要订单的最新状态、云厂商提供的账单也是你最终使用的资源来计价的(它不会包含你中途选择了却没有创建的资源)。如此,我们基于事件存储和事件溯源的系统在查询性能上面肯定是落后于基于状态存储的系统的。
所以我们即希望于保留用户操作的所有事件,也希望拥有一个高性能的查询系统,一句话,我们要事件存储也要状态存储,这样我们的业务才完整。CQRS就应运而生,如下图(此图片来自于网络)
我们的命令端使用事件存储,查询端使用状态存储,中间使用事件总线通信。这里新产生了一个概念:命令,命令是对象行为,是它产生了事件。
基于CQRS的系统结构,我们即保留的所有的事件数据,让我们的系统拥有了事件溯源的能力,又保留了状态存储的查询优势,二者兼得。
总结
基于商业目的,我们认为用户的任何操作都是有价值的,而基于审计日志、CDC等功能无法满足于我们的分析需求,至此基于事件的存储模型应运而生,而为兼容于查询需求我们将事件通过聚合形成状态,这让我们拥有了事件溯源的额外能力,除此以外我们使用打版本的操作来节约溯源的算里,但即便如此也无法满足我们高性能查询需求,于是提出了CQRS的系统结构。
后面的文章我们将继续介绍关于Event Sourcing和CQRS的落地技术
Event Sourcing 和 CQRS相关推荐
- java event sourcing_深入浅出Event Sourcing和CQRS
原标题:深入浅出Event Sourcing和CQRS Event Sourcing也叫事件溯源,是这些年另一个越来越流行的概念,是大神Martin Fowler提出的一种架构模式.简单来说,它有几个 ...
- Event Sourcing和CQRS实现
Event Sourcing和CQRS实现 文章参考自: https://github.com/soooban/AxonDemo 相关资料: https://blog.csdn.net/quguang ...
- cqrs java_深入浅出Event Sourcing和CQRS
Event Sourcing也叫事件溯源,是这些年另一个越来越流行的概念,是大神Martin Fowler提出的一种架构模式.简单来说,它有几个特点: 整个系统以事件为驱动,所有业务都由事件驱动来完成 ...
- [外文理解] DDD创始人Eric Vans:要实现DDD原始意图,必须CQRS+Event Sourcing架构。
原文:http://www.infoq.com/interviews/Technology-Influences-DDD# 要实现DDD(domain drive design 领域驱动设计)原始意 ...
- CQRS, Task Based UIs, Event Sourcing agh!
原文地址:CQRS, Task Based UIs, Event Sourcing agh! Many people have been getting confused over what CQRS ...
- (转)DDD CQRS和Event Sourcing的案例:足球比赛
原文链接: https://www.jdon.com/44815 在12月11日新的有关DDD CQRS和Event Sourcing演讲:改变心态- 以更加面向对象视角看待业务领域建模中,作者以足球 ...
- 领域驱动设计的实践 – CQRS Event Sourcing
1.前言 领域驱动(Domain – Driven Design)设计的理念在于建立一系列既符合软件所处领域本身又适合软件分析开发需要的领域模型.命令查询与职责分离(Command Query Res ...
- 对LMAX架构以及Event Sourcing模式的一些新思考和问题的记录
最近又学习了一下LMAX架构,让我对该架构以及event sourcing模式又有了很多新的认识和疑问. 注:如果不知道什么是lmax架构和event sourcing模式的看官可以自己先去查查资料: ...
- 分布式事务实践 解决数据一致性 分布式事务实现:Event Sourcing模式
详细介绍了分布式事务实现的模式中的Event Sourcing模式,并通过完整实例演示了Event Sourcing模式下,实现微服务系统的分布式事务的完整过程. 8-1 事件溯源模式介绍 8-2 事 ...
最新文章
- python 命令-python常见命令
- linux mysql 5.7.12_Centos 6.7 安装 mysql 5.7.12详细介绍
- 【网络安全】反序列化漏洞底层扩展与制作WebShell
- termux怎么安装python库_Python termux-apt-repo包_程序模块 - PyPI - Python中文网
- 误操作导致系统只剩下lo
- javascript中的一些核心知识点以及需要注意的地方
- 谷歌推Tacotron 2,搞定绕口令,效果优于WaveNet
- MSchart控件在Win7 64位操作系统上的注册方法
- 电脑同时上内外网——设置教程(附内外网优先级设置)
- matlab如何选局部最大值,如何在MATLAB中的图像中找到局部最大值?
- 国内常用开源镜像站点【推荐使用阿里巴巴开源镜像站】
- Oracle之数据排序
- QEMU 7.1发布
- 多媒体计算机主要有哪些基本特性,多媒体计算机的基本特性
- 学习分享——基于深度学习的NILM负荷分解(一)对DL的看法准备工作
- dz plugin.php,DZ支付积分充值插件 Discuz码支付免签约即时到账插件 Discuz手机支付插件...
- uniapp开发微信小程序保存图片带权限判断
- 大衣哥谷传民纷争,若和合国际收购《火火的情怀》收购价格受关注
- Python案例——将普通视频变成动漫视频
- “模拟飞行”的玩法(ArcGIS)