2 回答
TA贡献1943条经验 获得超7个赞
但是,如果我发送另一个具有相同 Id 的 CreateCar 命令,则该命令无法由聚合验证(即给定的 id 已经存在)。随后它会简单地触发一个新的 CarCreated 事件。这是谎言。
Axon 实际上会为您处理这些。当聚合发布事件时,它不会立即发布到其他组件。它在工作单元中暂存,等待处理程序执行完成。处理程序执行后,会调用许多“准备提交”处理程序。其中一个存储聚合(使用事件溯源时无操作),另一个是事件的发布(在事务范围内)。
取决于您是否使用事件溯源,将聚合实例添加到持久存储将失败(重复键),或者创建事件的发布将失败(重复聚合标识符 + 序列号)。
TA贡献1982条经验 获得超2个赞
如果汽车已经存在,确保 CreateCar 命令失败的最佳方法是什么?当然,我可以先检查存储库,但这不会阻止竞争条件......
没有魔法。
如果您要避免不正当写入,那么您要么需要获取数据存储上的锁,要么需要具有compare and swap语义的数据存储。
使用锁,您可以保证在读取存储中的数据和随后的写入之间不会发生冲突的更新。
lock = lock_for_id id
lock.acquire
Try:
Option[Car] root = repository.load id
switch root {
case None:
Car car = createCar ...
repository.store car
case Some(car):
// deal with the fact that the car has already been created
}
Finally:
lock.release
您希望每个聚合都有一个锁,但是创建锁的条件与创建聚合的条件相同。因此,您最终可能会使用粗粒度锁之类的东西来限制对操作的访问。
通过比较和交换,您可以将争用管理推向数据存储。您不是向商店发送PUT,而是发送有条件的 PUT。
Option[Car] root = repository.load id
switch root {
case None:
Car car = createCar ...
repository.replace car None
case Some(car):
// deal with the fact that the car has already been created
}
我们不再需要锁,因为我们正在为存储精确描述需要满足的前提条件(例如If-None-Match: *)。
事件存储通常支持比较和交换语义;将新事件“附加”到流上是通过制作一个查询来确定尾指针的预期位置,并使用特殊编码的值来标识预期创建流的情况(例如,事件存储支持ExpectedVersion.NoStream语义)。
添加回答
举报
