如何确保在最终一致的系统中一致的客户端读取?

时间:2012-04-30 07:08:16

标签: cqrs eventual-consistency

我正在深入研究CQRS,我正在寻找有关如何在最终一致系统中解决客户端读取的文章。例如,考虑用户可以将商品添加到购物车的网上商店。如果命令“AddItemToCart”的实际处理是异步完成的,如何确保客户端显示购物车中的项目?我理解调度命令异步和基于域事件更新读取模型异步的原则,但我没有看到如何从客户端角度处理它。

3 个答案:

答案 0 :(得分:4)

有几种不同的方法可以做到这一点;

等待用户直到

只需轮询服务器,直到更新读取模型。这与Ben所表现的类似。

通过2PC确保一致性

你有一个支持DTC的队列;并且您的命令首先放在那里。然后他们;执行,发送事件,读取模型更新;所有内部交易。虽然你没有用这种方法获得任何东西,所以不要这样做。

欺骗客户

将读取的模型放在客户端的本地存储中,并在发送相应的事件时更新它们 - 但您仍然期待此事件,因此您已经更新了购物车的javascript视图。

答案 1 :(得分:2)

我建议您查看Microsoft Patterns & Practices team's guidance on CQRS。虽然这仍在进行中,但他们已经为您提出的问题提供了一个解决方案。

他们对需要反馈的命令的方法是异步提交命令,重定向到另一个控制器操作,然后轮询读取模型以获得预期的更改或发生超时。这是使用Post-Redirect-Get模式,它可以更好地与浏览器的前进和后退导航按钮配合使用,并在MVC控制器开始轮询之前为基础架构提供更多时间来处理命令。

使用ASP.NET MVC 4异步控制器的RegistrationController示例代码。

[HttpGet]
[OutputCache(Duration = 0, NoStore = true)]
public Task<ActionResult> SpecifyRegistrantAndPaymentDetails(Guid orderId, int orderVersion)
{
    return this.WaitUntilOrderIsPriced(orderId, orderVersion)
        .ContinueWith<ActionResult>(

        ...

    );
}

...

private Task<PricedOrder> WaitUntilOrderIsPriced(Guid orderId, int lastOrderVersion)
{
    return
        TimerTaskFactory.StartNew<PricedOrder>(
            () => this.orderDao.FindPricedOrder(orderId),
            order => order != null && order.OrderVersion > lastOrderVersion,
            PricedOrderPollPeriodInMilliseconds,
            DateTime.Now.AddSeconds(PricedOrderWaitTimeoutInSeconds));
}

我可能会使用AJAX轮询而不是在服务器上阻止Web请求。

答案 2 :(得分:2)

发表-重定向-获取

您希望在调用Get之前按时执行save命令。如果命令在后端完成10秒钟但在1秒内调用Get会怎样?

本地存储

在命令执行时将命令的结果存储在客户端上,您假设该命令将无错误地通过。如果后端在处理命令时遇到错误怎么办?那么你在当地所拥有的并不是一致的。

<强>轮询

轮询似乎是实际符合最终一致性的选择;你不假装或假装。您的轮询机制可以是异步的,作为您网页的一部分,例如购物车页面组件轮询直到它获得更新而不刷新页面。

<强>回调

如果客户端能够接收到这样的内容,您可以引入类似Web挂钩的内容来回调客户端。一旦命令被后端接受,通过提供关联Id,一旦命令完成处理,后端就可以通知前端命令的状态以及关于命令是否进行的关联Id通过成功与否。使用这种方法不需要任何类型的轮询。