【问题标题】:Does event store store state?事件存储是否存储状态?
【发布时间】:2018-06-15 10:42:41
【问题描述】:

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

Using an RDBMS as event sourcing storage

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

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

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

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

Data里面到底存储了什么,是整个对象的状态还是序列化的事件?

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

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

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

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

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

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

【问题讨论】:

  • 我希望您知道以任何形式存储Age 都是一个坏主意。这保证每年至少需要更换一次。出生日期应该只需要因错误而更改,并且可以轻松地推算年龄。

标签: event-sourcing


【解决方案1】:

Data里面到底存储了什么,是整个对象的状态还是序列化的事件?

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

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

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

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

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

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

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

视情况而定。

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

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

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

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

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

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

【讨论】:

  • 在我看来,PersonChangedEvent 仍然包含所有可以更改的人员属性。即使他们没有改变
  • 例如我只更改年龄,但事件将同时包含姓名和年龄
  • 好答案,比我解释得更好!
【解决方案2】:

要回答您的问题,存储的事件应该只是事件数据。不是对象状态。 当您需要处理实体时,您会阅读所有事件并应用它们以每次获取最新状态。所以事件应该只与事件数据一起存储。 (ofc 连同 AggregateId、Version 等)

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

希望对您有所帮助!

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

【讨论】:

  • 所以当我创建一些东西时,事件将包含整个对象,但是如果我更改一些属性示例它是如何工作的:我有 Person 并且我更改了那个人的名字,事件会是什么样子.
  • 应该是代表单个属性变化的事件吗?喜欢 PersonNameChangedEvent?或者只是一个 PersonChangedEvent
  • 我认为上面的答案在答案的最后部分描述了这一点。在消息传递中(事件设计从消息设计中吸取了很多教训),我们经常设计带有可选字段的模式。所以事件模式可以超级灵活......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多