尽管在会议管理系统开发的早期阶段,CommandBus和EventBus类的实现非常相似,您可能想知道为什么我们同时拥有这两个分开的类,因为团队预计它们在未来会出现区别。
Markus(软件开发人员)发言:在调用处理程序的方式和为它们捕获什么样的上下文方面可能存在差异:命令可能希望捕获额外的运行时状态,而事件通常不需要这样做。由于这些潜在的未来差异,我不想统一实现。我以前也遇到过这种情况,一旦有进一步的要求时,我就把它们分开。 这个方案的可扩展性如何?
使用这种方案,您可以在不同的Azure工作角色实例中运行SubscriptionReceiver类的多个实例和各种处理程序,这使您能够扩展您的解决方案。您还可以在不同的Azure工作角色实例中拥有CommandBus、EventBus和TopicSender类的多个实例。
有关扩展Azure服务总线基础设施的信息,请参阅MSDN上的Best Practices for Performance Improvements Using Service Bus Brokered Messaging
这个方案的健壮性如何?方案使用Azure服务总线的代理消息传递选项来提供异步消息传递。服务总线总是可靠地存储消息,直到用户连接并获取这些消息。
另外,从队列或Topic订阅获取消息的peek/lock方法为消息消费者在处理消息失败的场景中增加了可靠性。如果消费者在调用Complete方法之前失败,则当消费者重新启动时,任然可以处理该消息。
怎么划分Topic和订阅的粒度?当前的实现是系统中的所有命令都使用一个Topic(会议/命令),为系统中的所有事件也使用一个Topic(会议/事件)。每个Topic都有一个订阅,每个订阅接收发送到该主题的所有消息。CommandProcessor和EventProcessor类负责将消息传递给正确的处理程序。
将来,团队会研究使用多个Topic,例如,为每个限界上下文使用单独的命令Topic和多个订阅(一个事件类型一个订阅)。这些替代方案可以简化代码,并促进扩展应用程序跨多个Azure工作角色,来工作。
Jana(软件架构师)发言:使用多个Topic、订阅或队列没有额外的成本。Azure服务总线是根据发送的消息数量和从Azure子区域传输的数据量来进行计费的。 命令和事件如何序列化?
Contoso会议管理系统使用Json.NET来序列化和反序列化。有关应用程序如何使用序列化工具的详细信息,请参阅参考指南中的“参考实现中使用的技术”
您应该考虑是否需要为命令使用Azure服务总线。命令通常使用在有边界的上下文中,您可能不需要跨进程边界发送它们(在写入端,您可能不需要额外的层),在这种情况下,您可以使用内存队列来传递命令。” -- Greg Young,与模式与实践团队的对话 对测试的影响因为这是团队处理的第一个限界上下文,所以关键一点是,如果团队希望采用测试驱动开发(TDD),那么如何进行测试。下面是两名开发人员之间的对话,他们讨论了在没有事件源(ES)的情况下实现CQRS模式时如何进行TDD,对话总结了他们的想法:
开发人员1:如果我们使用事件源(ES),那么在创建领域对象时使用TDD方法将会很容易。测试的输入将是一个命令(可能起源于UI),然后我们可以测试领域对象是否触发了预期的事件。然而,如果我们不使用事件源,我们就没有任何事件,领域对象的行为是通过ORM层将其更改持久化到数据存储中的。
开发人员2:那么我们为什么不发起事件呢?我们没有使用事件源(ES)并不意味着我们的领域对象不能引发事件。让领域对象引发事件,然后我们可以按照通常的方法设计测试,以检查响应命令时触发的正确事件。
开发人员1:这难道不是让事情变得比需要的更复杂了吗?使用CQRS的动机之一就是简化事情!现在我们有了领域对象,它们需要使用ORM层来持久化它们的状态。然后我们又要引发事件来报告它们所持久化的内容,因为这样我们就可以运行单元测试了。
开发人员2:我明白你的意思。
开发人员1:我们可能在如何进行测试上遇到了瓶颈。也许我们不应该基于领域对象的预期行为来设计测试,而是应该考虑在领域对象处理命令之后测试它们的状态。
开发人员2:这应该很容易做到,毕竟,领域对象把我们想要检查的所有数据都存储在属性中,以便ORM可以将正确的信息持久化到存储中。
开发人员1:所以我们只需要考虑在这个场景中使用另一种不同的风格进行测试。