Skip to content

事件溯源 (Event Sourcing)

只持久化领域事件,不持久化聚合,当命令到达,要载入聚合时,通过回溯领域事件,重建出聚合。

采用事件溯源的优点是什么?

  • 很容易实现高性能写。由于只持久化领域事件,而领域事件是天然的时间序列不可变数据,对于数据库来说它只会插入,不会更新,对于持久化设备来说,它可以顺序写。不管是机械硬盘还是SSD,顺序写的时候都能够获得比随机写更高的性能。
  • 不用再考虑聚合的持久化问题,聚合设计更加灵活。聚合持久化实际上是有难度的,如果不借助于ORM,会有一定的编码量;而使用ORM,又意味着聚合内的状态数据要能够容易被ORM管理,使聚合的设计受限。采用事件溯源将再也没有这样的问题,完全自由设计聚合内部的状态。
  • 很容易给聚合实现时间机器,从而查看任意时刻聚合的状态。这个能力有助于去做一些业务分析,debug,甚至是某些业务场景下必须的能力。

如何通过领域事件重建出聚合?

仓储的实现代码里,载入领域事件后,把领域事件按照顺序传递给聚合,让聚合根据这些事件去计算内部的状态应该如何演变,等所有领域事件都被聚合消费完了,重建过程就完成了。之前建模的时候,已经确保了在逻辑上,聚合一定能够通过它的领域事件重建出来。如果发生无法重建的情况,那一定是之前的建模遗漏了什么,要调整建模方案。

领域事件数量那么多,会不会导致聚合载入太慢?

  • 载入聚合的时候,是要知道聚合的id,载入和这个id关联的事件,并不是整个系统发生的所有的事件。对于一些聚合,实际业务场景中可能发生过的事件的数量是有限可控的,不会引发性能问题。
  • 对于可能产生大量事件的聚合,可以采用快照机制,即每隔一段领域事件,给当时的聚合的状态存储起来,下次从快照处,读取之后的领域事件来重建,从而提升重建效率。

如果使用了快照,岂不是意味着又持久化了聚合,那又何必使用事件溯源呢?

  • 聚合的快照数据是非结构化的,因为它只用来提供给恢复聚合用,不承担任何查询责任。比如把聚合对象的状态序列化成json存储。这种方式大大简化聚合状态的持久化开发量。总体还是要比不用事件溯源的持久化难度低的。
  • 快照是可以异步非实时生成的,从而可以继续享受事件溯源的高性能写优点。

采用事件溯源是不是必须采用CQRS?

一般来说是的。只要有查看领域事件意外的查询需求,都必须采用CQRS了。

什么情况下采用事件溯源?

  • 需要提高命令的处理速度
  • 需要更灵活自由的聚合内部设计
  • 需要聚合时间机器,回溯任意时刻聚合状态的能力

可以只对部分聚合采用事件溯源吗?

当然可以。从聚合的外部来看,还是通过仓储载入聚合,给聚合发消息,再把聚合存回仓储,是否使用事件溯源并没有什么差别。

采用事件溯源的难点是什么?

  • 转变思维习惯
  • 必须严格的建模,保证能够重建聚合