SQL事件存储-每个聚合一个流

时间:2019-09-16 04:32:14

标签: database-design event-sourcing event-store

我想知道当每个聚合获得自己的事件流时,如何将SQL数据库设计为事件存储:

每个Stopwatch stopwatch = Stopwatch.StartNew(); HashSet<FieldInfo> fieldInfos = new HashSet<FieldInfo>(); //Loop over every single bit combination for (int i = 0; i < 0x01111111; i++) { BindingFlags f = (BindingFlags) i; FieldInfo[] infos = thread.GetType().GetFields(f); foreach (FieldInfo info in infos) fieldInfos.Add(info); } Debug.Log($"Time to search: {stopwatch.Elapsed.TotalMilliseconds:n0}ms"); stopwatch.Restart(); Debug.Log(fieldInfos.Count); foreach (FieldInfo info in fieldInfos) { Debug.Log(info.Name); Thread.Sleep(10); } Debug.Log($"Time to log: {stopwatch.Elapsed.TotalMilliseconds:n0}ms"); 代表一个事件

X

我会为每个用户流创建一个单独的表吗?

还是我以某种方式管理一个表中的所有用户?


要求:

  • 附加事件
  • 按时间顺序保留事件
  • 读取用户的所有事件
  • 读取事件后具有特定编号的用户的所有事件
  • 删除事件(很少)

P.S。如果有人为我的要求提供一些示例查询,那将是很好的

1 个答案:

答案 0 :(得分:2)

我知道,大多数SQL支持的事件存储示例都使用单个事件表来存储数据库中的所有聚合。基本形状是用于保存事件的BLOB列,然后是用于有用的元数据的其他列。

eventide-project是一个很好的参考,该项目具有一个开放源代码模块,用于在Postgresql:message-store-postgres-database之上构建消息存储。 messages table非常简单:将jsonb列分别用于数据和元数据,然后添加其他列以支持对常见用例进行有效查询。

Gabriel Schenker在Los Techies撰写了一系列文章:How We Got Rid of the Database

Konrad Garus写道:Achieving Consistency in CQRS with a Linear Event Store

  

按时间顺序保留事件

对于单个流,通常通过为流中的每个事件写一个本地序列号来维持顺序。您需要做一些工作,以确保在对同一流执行并发写入时,先写者获胜。流中的每个事件与所有具有较低本地序列号的事件都具有happens-after关系。

在多个流中,不一定严格定义“顺序”。不能保证通过不可靠的网络发送的消息将按其发送顺序到达。 Eventide使用global_position列来跟踪将事物写入数据库的顺序。

在事件上添加时间戳可以使您对时间进行某种程度的测量,但是,当然,如果使用不同的时钟来测量时间,则不能保证排序的有效性。

  

很少删除事件

从事件表中删除行可能会使您不高兴。考虑将行标记为已删除或覆盖事件数据,而不要在记录中引入空白。

事件源的通常纪律是通过添加适当的补偿事件来修复业务错误,而不是尝试更改历史记录。

  

您需要根据法律(GDPR)删除数据

社区中对此进行了大量讨论。真正的答案是,您需要使数据隐私成为设计中的头等大事。用不包含敏感信息的副本替换事件是一种可能的方法,将敏感信息存储在事件历史记录之外(在专用机密管理器中)是另一种可能性。

注意:以上内容不应被视为适用于任何司法管辖区的法律建议。