最近我参加了CQRS研讨会,让我想知道在使用事件采购时如何存储事件。
我认为在每个单个事件之后整个应用程序应该处于有效状态(否则重放功能是无用的)。这意味着事件应该按照它们发生的顺序存储。我还认为所有聚合的事件顺序很重要。例如,用户可以制作产品和订单(产品和订单都是单独的聚合,通过ID订购参考产品)。这意味着“创建产品事件”应存储在“将产品添加到事件”之前。否则,重播可能会导致无效状态,即产品存在之前存在引用产品的订单。
这种情况如何处理?您是否应始终使用同步方法将事件发送到数据库,例如通过锁定数据库?这个解决方案可扩展吗或者您应该将每个聚合的事件存储在不同的表中吗?但是你如何确保订购呢?另一种选择是存储每个事件的时间,并按此排序。 PC上的定时器精度是否足够高,能够做到这一点?
答案 0 :(得分:3)
您可以查看NEventStore的来源,它可以满足您的需求。所有事件都按照它们提交的顺序存储,事件存储并不真正关心您的特定业务对象,它只是原子地保留了一系列事件。您的应用程序可以按正确的顺序生成它们。
另外,请注意您的业务对象应该是正确的聚合根,这意味着,它最多只有另一个AR的id。一个AR不关心另一个,但业务流程会告诉您应该以什么顺序执行操作,例如:OrderCreated =>订单已被保留=>创建发票(参考订单)=>发票已创建。
例如,用户可以制作产品和订单(产品和订单都是单独的聚合,通过ID订购参考产品)。
不,它不能。它需要产品信息才能从中创建订单。客户不会在购物车中添加隐形产品。客户也不会创建产品。
要讨论这些内容还有很多东西,但为了使事情正常工作,你真的需要正确设计的域对象,并了解事件源(ES)究竟是什么(提示:它将对象状态表示为事件流)。
顺便说一句,在使用ES时,您应该专注于在对象级别正确实现它,不应该构建自己的事件存储。
答案 1 :(得分:2)
我知道这是一个老话题,但你对此提出质疑是绝对正确的。事件存储中事件的持久性与您的问题无关。时间戳是无效的解决方案,用户在逻辑上可以执行的操作与您的问题无关。
你的问题,如果我错了,纠正我的是,一旦事件存储通过可扩展的东西发布事件(例如消息总线) - 你如何保证非规范化器以正确的顺序接收事件。消息总线通常不保证事件的排序。
答案是你不能,所以我建议的一个选项是提供事件的版本号,并且只有在最后一个已知状态的版本号与事件中的版本号匹配时才更新预计模型。如果没有,请将事件配置为在等待下一个事件时重试。
答案 2 :(得分:0)
例如,用户可以制作产品和订单(产品和产品) 订购两个单独的聚合,订单参考产品通过 一个ID)。
CQRS,Command Query Responsability Segregation,意味着您使用两种不同的模型,一种用于写入模型,一种用于读取模型。因此,如果您想将产品添加到购物车中(例如AddProductToCart
),则产品必须位于阅读模型中。
重点是最终的一致性。例如,由于读取模型和写入模型尚未同步,用户无法在几秒钟前看到您已添加到库存中的产品。他们将最终同步 (当他们成为您的用户时,他们会看到您添加的产品)。