首页 > 解决方案 > 事件存储是否存储状态?

问题描述

从理论上讲,当使用事件源时,您不存储“状态”而是存储事件。但是我在许多实现中看到,您将状态快照以 JSON 或 BLOB 之类的格式存储在列中。例如:

使用 RDBMS 作为事件源存储

事件表具有存储整个对象的数据列。对我来说,这就像在某个事件发生的给定时间存储状态。

还有这张照片(取自Streamstone):

在此处输入图像描述

它具有具有序列化状态的数据列。所以它也存储状态但在事件中?

那么如何从初始状态重播,如果我可以简单地选择一些事件并访问数据直接获取状态。

Data 中究竟存储了什么,是整个对象的状态还是序列化事件?

假设我有一个人对象(在 C# 中)

public class Person
{
    public string Name { get; set }
    public int Age { get; set; }
}

当我创建一个人或更改姓名或年龄等属性时,我的事件应该存储什么。

当我创建一个 Person 时,我很可能会发送PersonCreatedEvent带有初始状态的东西,因此是整个对象。

但是,如果我更改姓名或年龄,它们应该是 2 个单独的事件还是只有 1 个呢?PersonChangedEventPersonChangedAgeEventPersonChangedNameEvent

在这种情况下应该在事件中存储什么?

标签: event-sourcing

解决方案


Data 中究竟存储了什么,是整个对象的状态还是序列化事件?

这通常是事件的序列化表示。

一种思考方式:事件流类似于补丁文档流。当前状态的计算方法是从一些已知的默认状态开始,然后依次应用每个补丁——也就是“折叠”。可以通过选择流中的某个点并将补丁应用到该点来恢复以前的状态。

与补丁不同,事件的语义往往是特定于域的。所以Checked-InChecked-Out而不是PatchedPatched

我们通常保持事件紧凑 - 您通常不会记录未更改的状态。

在您的领域特定事件语言中,事件中字段的语义可能是“替换”——例如,当记录名称的更改时,我们可能只存储整个新名称。在其他情况下,改为“汇总”可能是有意义的——使用诸如帐户余额之类的东西,您可能会记录贷方和借方,从而得出余额,或者您可能会更新总余额(如仪表)。

在大多数成熟领域(银行、会计)中,领域语言具有用于记录更改的语义,而不是级别。我们将新条目写入分类帐,我们将新条目写入支票簿寄存器,我们在帐户对帐单中读取已完成和待处理的交易。

但是,如果我更改姓名或年龄,它们应该是 2 个单独的事件还是只有 1 个呢?PersonChangedEvent 或 PersonChangedAgeEvent 和 PersonChangedNameEvent?

这取决于。

交易产生多个事件并没有错。

拥有一个可以以多种方式重用的单一事件模式并没有错。

拥有不止一种改变相同字段的事件并没有错。 NameLegallyChanged并且SpellingErrorCorrected可能是业务的一个有趣区别。

激发基于任务的 UI的许多相同问题也适用于事件模式的设计。

在我看来,PersonChangedEvent 仍然会包含所有可以更改的人员属性。即使他们没有改变

在消息传递中(事件设计从消息设计中吸取了很多教训),我们经常设计带有可选字段的模式。因此,事件模式可以非常灵活,而事件的任何单独表示都将是紧凑的(仅限于感兴趣的信息)。


推荐阅读