我仍在努力解决与 CQRS 风格架构相关的基本(和已解决)问题:
我们如何实现依赖一组聚合根的业务规则?
以预订应用程序为例。它可以帮助您预订音乐会门票、电影座位或餐厅餐 table 。在所有情况下,只会有数量有限的“商品”出售。
假设该事件或地点非常受欢迎。当新事件或时段开始销售时,预订很快就会到达 - 也许每秒有很多。
在查询方面,我们可以大规模扩展,并将预订放入队列中,由自治组件异步处理。首先,当我们从队列中取出预留命令时,我们将接受它们,但在某个时间我们将不得不开始拒绝其余命令。
我们如何知道何时达到限制?
对于每个预订命令,我们都必须查询某种商店以确定我们是否可以满足该请求。这意味着我们需要知道当时我们已经收到了多少预订。
但是,如果域存储是非关系数据存储,例如Windows Azure 表存储,我们不能很好地执行 SELECT COUNT(*) FROM ...
一种选择是保留一个单独的聚合根来简单地跟踪当前计数,如下所示:
- AR:预订(谁?有多少人?)
- AR:事件/时段/日期(总计)
第二个聚合根将是第一个聚合根的非规范化聚合,但是当底层数据存储不支持事务时,这些很可能在大容量场景中不同步(这就是我们的情况)正在尝试首先解决)。
一种可能的解决方案是序列化预订命令的处理,以便一次只处理一个,但这违背了我们的可扩展性(和冗余)目标。
这样的场景让我想起了标准的“缺货”场景,但不同之处在于我们不能很好地将预订延期交货。事件一旦售完,就已经售完,所以我看不出有什么补偿措施。
我们如何处理这种情况?
最佳答案
经过一段时间的思考,我终于意识到,根本问题与 CQRS 的关系不大,而与不同 REST 服务的非交易性质相关。
归根结底就是这样一个问题:如果你需要更新多个资源,如果第二次写操作失败,如何保证一致性?
假设我们要按顺序向资源 A 和资源 B 写入更新。
- 资源 A 已成功更新
- 尝试更新资源 B 失败
第一次写操作遇到异常是不容易回滚的,那怎么办呢?捕获并抑制异常以对资源 A 执行补偿操作并不是一个可行的选择。首先,它实现起来很复杂,其次,它也不安全:如果由于网络连接失败而发生第一个异常,会发生什么?在这种情况下,我们也无法针对资源 A 编写补偿操作。
关键在于显式的幂等性。虽然 Windows Azure 队列不保证恰好一次语义,但它们确实保证至少一次语义。这意味着,面对间歇性异常,该消息稍后将重播。
在前面的场景中,会发生以下情况:
- 尝试更新资源 A。但是,重播会被检测到,因此 A 的状态不会受到影响。但是,“写入”操作成功。
- 资源 B 已成功更新。
当所有写入操作都是幂等时,可以通过消息重放来实现最终一致性。
关于azure - 在 CQRS 中实现基于集合的约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4355860/