CQRS & Event Sourcing在结算付款系统中的应用

CQRS

何谓CQRS?

Command Query Responsibility Segregation

其实就是一种读写分离,不同于数据库纯技术上的读写分离的概念,这里的读写分离是数据流在架构上的读写分离。

关于CQRS,我见过的最好的解释是这个样子的:

Storing data is normally quite straightforward if you don’t have to worry about how it is going to be queried and accessed; many of the complexities of schema design, indexing, and storage engines are the result of wanting to support certain query and access patterns. For this reason, you gain a lot of flexibility by sepa‐ rating the form in which data is written from the form it is read, and by allowing sev‐ eral different read views. This idea is sometimes known as command query responsibility segregation (CQRS).

不翻译,但凡翻译都会有偏差。以转账为例,画图举个例子就明白了:

imagepng

Event Sourcing

任何我们看到的事物的当前状态都是对过去一系列影响该状态的事件(event)的求和,包括我们的宇宙(《时间简史》里面说的)

event是真实发生的事件,是不可变的(immutable),而这一系列事件导致的事物的状态(state)是变化的。

state和event是任何事物的两个方面,下面的公式是我见过的对此(Event Sourcing)最好的解释:

imagepng

CQRS和Event Sourcing思想最精髓的地方在于:

  1. 事件不可变
  2. 由过去的一系列事件可以推导出当前的状态
  3. 状态可变
  4. 事件和状态可以根据查询视角选择合适的存储方式和存储结构

在结算付款系统中的应用

无论账单还是付款单,都有许多状态,这些状态分别对应一系列事件。

单拿账单来说,账单存在以下状态:

1:已创建 2:已关闭 3:已确认 4:已提交开票申请 5:发票审核已通过 6:已创建付款单

不同的业务场景,状态转移方式可能也不一样,比如有这样的:

1 -> 2 -> 3 -> 4 -> 5 -> 6

也有这样跳过发票直接创建付款单的

1 -> 2 -> 3 -> 6

还有这样跳过确认,关闭后直接创建付款单的:

1 -> 2 -> 6

对用户来说,可能只关系当前进度,那么用于用户查询的系统可以之存储账单最新的状态,而不必关心变更历史(事件历史)。

而需要事件溯源的场景,比如状态变更时间轴,像这样的:

imagepng

而忠实的的记录每一个事件,log append的方式最合适不过了,反映在数据库上,LSM Tree系列的都可以(当然mysql也可以,但是考虑到数据量和分表,还是算了吧),比如HBase,根据key,不管是哪种状态转移方式,直接一拎一串都出来了,而且是时间排序的。


悟来时见江海古,
苍崖行遍谒玄门。
向道偶题人间世,
一笛一剑一昆仑。