事件存储是否存储状态?

时间:2018-06-15 10:42:42

标签: event-sourcing

理论上,当使用事件采购时,你不会存储"状态"但事件。但是我在许多实现中都看到,您以一种格式(如JSON或BLOB)存储状态快照。例如:

Using an RDBMS as event sourcing storage

事件表具有存储整个对象的数据列。对我而言,它就像在某个事件发生的那段时间内存储状态一样。

此照片(取自Streamstone):

enter image description here

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

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

究竟是什么存储在数据中,它是整个对象的状态还是它的序列化事件?

让我们说我有一个人物对象(在C#中)

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

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

当我创建一个Person时,我很可能会发送类似PersonCreatedEvent的初始状态,所以整个对象。

但是,如果我更改名称或年龄应该是2个单独的事件还是1个? PersonChangedEventPersonChangedAgeEventPersonChangedNameEvent

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

2 个答案:

答案 0 :(得分:2)

  

究竟是什么存储在数据中,它是整个对象的状态还是它的序列化事件?

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

一种思考方式:事件流类似于patch documents流。通过从一些已知的默认状态开始然后依次应用每个补丁来计算当前状态 - 也就是a" fold"。通过在流中的某个位置选择一个点并将补丁应用到该点,可以恢复以前的状态。

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

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

在您的特定于域的事件语言中,事件中字段的语义可能是"替换" - 例如,当记录名称的更改时,我们可能只存储整个新名称。在其他情况下,聚合"可能是有意义的。相反 - 使用帐户余额之类的东西,您可以记录贷方和借方,从而获得余额,或者您可以更新总余额(如计量表)。

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

  

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

取决于。

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

拥有单一事件架构没有任何问题,可以通过多种方式重复使用。

有多种事件可以改变相同的字段,这没有什么不对。 NameLegallyChangedSpellingErrorCorrected可能是业务的有趣区别。

激励task based UIs的许多相同问题都适用于您的事件架构的设计。

  

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

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

答案 1 :(得分:1)

要回答您的问题,存储的事件应仅为事件数据。不是对象状态。 当您需要处理您的实体时,您会读取所有事件并应用它们以便每次都获得最新状态。因此,事件应仅与事件数据一起存储。 (ofc与AggregateId,版本等)

"对象状态"将是所有事件的计算,但如果您有一个监听所有已发布事件的Eventlistener,您可以为您填充单独的ReadModel。从用户角度查询和使用只读。

希望它有所帮助!

更新了更新问题的答案: 真的取决于您的模型,如果您同时更新年龄和名称,是的新年龄和名称值应存储在新事件中。 该事件应仅包含此数据"名称和年龄与aggregateId,版本等" 事件侦听器将专门侦听每个事件(创建,更新等),查找已存储的聚合读取模型,并仅更新这两个属性(在此示例中)。 对于createevent,您可以为读取模型创建对象。