Markus(软件开发人员)发言:
因为实现CQRS模式会导致对组成系统的许多不同部分的职责进行非常清晰的分离,所以我们发现添加优化和加强相对容易,因为许多必要的更改在系统中都非常容易定位。
当注册者创建一个订单时,她将访问UI中的以下页面序列。
注册页面。该页面根据最终一致的读模型显示会议的门票类型和当前可用的座位数量。注册者选择她想购买的每种座位类型的数量。
付款的页面。此页面显示订单摘要,其中包括一个总价和一个倒计时计时器,它告诉注册者座位将保留多久。注册者输入她的详细信息和首选的付款方式。
支付页面。这里模拟了一个第三方支付处理器。
注册成功页面。这将显示支付是否成功。它向注册者显示一个订单定位器代码,并链接到另一个页面,该页面允许注册者为参会者分配座位。
有关UI中的屏幕和流程的更多信息,请参阅第5章“准备发布V1版本”中的“基于任务的UI”一节。
在V2版本中,系统必须在注册页面付款页面之间处理以下命令和事件:
RegisterToConference
OrderPlaced
MakeSeatReservation
SeatsReserved
MarkSeatsAsReserved
OrderReservationCompleted
OrderTotalsCalculated
此外,MVC控制器在发送初始RegisterToConference命令之前通过查询读模型来填充订单,从而验证是否有足够的座位可用。
当团队使用Visual Studio Load Test和不同的用户负载模式来对应用程序做负载测试时,我们注意到高负载常常发生在UI等待领域完成其处理时和读模型接收写模型数据时。这样无法显示下一个页面。特别是,随着V2版本部署到中型的web和工作角色实例后,我们发现:
对于每秒少于5个订单的恒定负载模式,所有订单都在5秒的窗口内处理。
对于每秒8到10个订单之间的恒定负载模式,许多订单不能在5秒的窗口内处理。
对于每秒8到10个订单之间的恒定负载模式,角色实例使用得不够理想(例如CPU使用率很低)。
备注:从UI在服务总线上发送初始命令到读模型中出现定价订单,从而使UI能够向用户显示下一个屏幕。5秒是我们希望看到的最大等待时间。
为了解决这个问题,团队确定了两个优化目标:UI和领域之间的交互,以及基础设施。我们决定首先处理UI和领域之间的交互。当这不能充分提高性能时,我们还进行了基础设施优化。
优化UI团队与领域专家讨论了在UI向领域发送RegisterToConference命令之前,是否总是需要验证座位可用性。
Gary(CQRS专家)发言:
这个场景说明了与最终一致性相关的一些实际问题。读端(在本例中是定价订单视图模型)最终与写端保持一致。通常,当您实现CQRS模式时,您应该能够接受最终的一致性,而不需要在UI中等待更改传播到读取端。然而,在这种情况下,UI必须等待写模型传播到与特定顺序相关的读端信息。这可能表明原系统这一部分的分析和设计存在问题。
领域专家明确表示,系统应该在接受付款之前确认座位是否可用。Contoso不希望出售座位之后向注册人解释,这些座位是不可用的。因此,该团队寻找了简化流程的方法,直到注册者看到付款屏幕为止。
Beth(业务经理)发言:
这种谨慎的策略并不适用于所有情况。在某些情况下,即使不能立即完成订单,企业也可能宁愿接受这笔钱。企业可能知道库存很快就会补充,或者客户很乐意等待。在我们的场景中,尽管Contoso可以在没有票的情况下将钱退还给注册者,注册者也许仍然会购买机票,因为他以为系统已经确认过,这笔钱是没法退还的。所以这很明显是一个业务和领域专家要做的决策。
团队确定了对UI流的以下两个优化。
UI优化1大多数情况下,会议有足够的座位,注册者不必相互争夺来预订座位。随着大会的门票接近售罄,只有很短的一段时间内,报名者才会争夺最后几个座位。
如果会议有足够的可用座位,那么注册者到达付款界面却发现系统无法预订座位的风险就很小。在这种情况下,V2版本里,在到达付款页面之前执行的一些处理可以在付款页面上当用户输入信息的时候异步发生,这样就减少了注册者在看到付款页面前经历延迟的机会。