使用EventStoreDB实现事件溯源的Java开源项目


EventStoreDB 是事件溯源的数据库。这个GitHub存储库提供了一个使用 EventStoreDB 作为事件存储的事件源系统示例。

此示例使用受tech/uklon经验启发的高度简化的叫车领域模型。

  • 骑手可以在指定价格的路线上下订单。
  • 司机可以接受并完成订单。
  • 订单可以在完成前取消。

事件溯源
事件溯源将实体的状态保存为一系列不可变的状态更改事件。

每当实体的状态发生变化时,都会将新事件附加到事件列表中。

可以通过重放其所有事件来恢复实体的当前状态。
事件溯源最适合事件总数相对较少的短期实体(如订单)。
通过重放其所有事件来恢复短期实体的状态不会对性能产生任何影响。因此,短期实体不需要恢复状态的优化。
对于具有数千个事件的无休止存储的实体(如用户或银行账户),通过重播所有事件来恢复状态并不是最佳的,应该考虑快照。
快照是一种优化技术,其中还保存了聚合状态的快照,因此应用程序可以从快照中恢复聚合的当前状态,而不是从头开始。

事件溯源中的实体也被称为聚合。
同一聚合的一系列事件也被称为流。

Event Store的要求

  • 永久存储。永久存储事件。
  • 乐观并发控制。防止丢失更新异常(写-写冲突)。
  • 加载当前状态。从事件存储中加载特定聚合 ID 的所有先前事件。
  • 按聚合类型订阅所有事件。而不是订阅代表聚合的单个事件流。
  • 检查站。处理后存储事件偏移量(流中的位置)。在应用程序重新启动后,从最后一个已知位置订阅,而不是从流开始。

解决方案架构
EventStoreDB原生支持追加事件、并发控制、读取事件、事件的持久化订阅。

该解决方案的重要部分是EventStoreDB的持久性订阅。

持久性订阅的目的是向连接的订阅者实时提供事件,并由服务器维护。持久性订阅保留了订阅者在服务器上开始获取事件的最后已知位置。

持久性订阅可以是负载平衡的,并且可以并行处理事件。为了使服务器能够平衡订阅者的负载,它使用了消费者组的概念。

有一个Pinned消费者策略,旨在与索引投影(如系统$by_category投影)一起使用。

事件流id被哈希到分配给单个客户的1024个桶中的一个。当一个客户端断开连接时,它的桶被分配给其他客户端。当一个客户端连接时,它被分配到一些现有的桶中。这样做的目的是为了保持工作负载的平衡。

Pinned消费者策略的主要目的是减少并发和排序问题的可能性,同时保持负载平衡。这并不是一种保证,你应该处理通常的排序和并发问题。