使用Scala和不可变对象的事件源:它是否有效?

时间:2017-02-09 13:15:41

标签: scala immutability cqrs event-sourcing

我正在尝试使用Scala进行事件采购(我在这两个领域都是新手)。

我希望尽可能保持一切不变,包括聚合根。 作为基础,我遵循Greg Young的ES和CQRS(https://github.com/gregoryyoung/m-r的示例,在C#中实现)并将此代码“翻译”为Scala。

问题是关于事件存储中的对象重生:

由于我的聚合根是不可变的,因此对于特定实体的每个事件,都会创建新对象。因此,例如,要重新生成具有100个事件的实体,应创建此实体的100个实例,最后一个实例将是最终状态。

效率高还是开销过高?

(我认为重新生成的最大事件数将为100,因为我将存储快照)。

这是我的代码:

活动特质

trait Event {

}

聚合的基类

abstract class AggregateRoot {

  ...
  // HERE ENTITY IS REGENERATED FROM EVENTS AND EACH TIME NEW INSTANCE IS CREATED FOR EACH ENTITY  

  def loadFromHistory(history: Seq[Event]): AggregateRoot = {
    var aggregate = this
    history.foreach(e => aggregate = applyChange(e, false))
    aggregate
  }

  def applyChange(event: Event, isNew: Boolean): AggregateRoot
  ...
}

产品汇总

case class Product(id: Long, name: String, status: Int, uncommittedChanges: Seq[Event]) extends AggregateRoot {

  def changeName(name: String): Product = {
    applyChange(ProductNameChangedEvent(id = this.id, name = name))
  }

  def applyChange(event: Event, isNew: Boolean = true): Product = {
    val changedProduct = event match {
      case e: ProductCreatedEvent => applyChange(e)
      case e: ProductNameChangedEvent => applyChange(e)      
      case _ => throw new Exception("No event applier")
    }
    // ALSO HERE I'M COPING ALL THE LOCAL (NEW) CHANGES (THAT ARE NOT STORED IN EVENT STORE YET)
    if (isNew) changedProduct.copy(uncommittedChanges = uncommittedChanges :+ event) else changedProduct
  }

  def applyChange(event: ProductCreatedEvent): Product = {
    this.copy(id = event.id, name = event.name)
  }

  def applyChange(event: ProductNameChangedEvent): Product = {
    this.copy(id = event.id, name = event.name)
  }

  ...

}

1 个答案:

答案 0 :(得分:1)

嗯......当你谈到效率时,它总会归结为数据结构的选择以及你如何优化?

在这种特殊情况下,Product看起来像这样,

case class Product(id: Long, name: String, status: Int, changes: Seq[Event])

当您在changes累积事件时,您的效率将由此处的数据结构决定。你不能只留下一般Seq

现在有一个重要的问题你应该问一下 - 你对changes有什么用例?

嗯......除了所有不为人知的事情......我可以看到,只要您收到新活动,就要将其添加到changes。这意味着如果您收到的频繁事件多于您需要优化将其添加到changes

的操作所需的事件

所以......让我们假设您正在使用changes: List[Event]

现在,您必须使用prepend这是一个恒定时间 - C而不是append,即O(n)。

// so this line 
if (isNew) changedProduct.copy(changes = changes :+ event) else changedProduct

// will have to changed to
if (isNew) changedProduct.copy(changes = event +: changes) else changedProduct

但现在你changes列表将包含颠倒顺序的事件......所以你必须记住,在重建你的状态时你会以相反的顺序应用它们。

要了解有关各种集合的某些操作的时间复杂性的更多信息,您应该看一下 - http://docs.scala-lang.org/overviews/collections/performance-characteristics.html

请记住,它上面的“效率”没有任何意义。你应该总是问 - “效率......但做什么?”