更常见的场景是聚合之间存在一对多的关系,而不是一对一的关系。在这种情况下,不可能共享聚合ID,相反,“一”的聚合可以存储“多”聚合的ID列表,而“多”的每个聚合可以存储“一”聚合的ID。
当聚合存在于不同的限界上下文中时,共享聚合ID是很常见的。如果您在不同的限界上下文中使用聚合对同一个现实实体的不同方面建模,那么它们共享相同的ID是有意义的。
Greg Young --与模式和实践团队的对话
基于任务的用户界面UI的设计在过去的十年中有了很大的改进。应用程序比以前更容易使用,更直观,导航也更简单。一些UI设计指南的例子可以帮助您创建这样的现代的、用户友好的应用程序,如Microsoft Inductive User Interface Guidelines和Index of UX guidelines。
影响UI设计和可用性的一个重要因素是UI如何与应用程序的其他部分通信。如果应用程序基于CRUD风格的体系结构,这可能会泄漏到UI。如果开发人员专注于CRUD风格的操作,这可能会导致出现类似下图(左边)中第一个屏幕设计所示的UI。
在第一个屏幕上,按钮上的文字反映了当用户单击Submit按钮时系统将执行的底层CRUD操作,而不是显示用户更关心的操作的文字。不幸的是,第一个屏幕还要求用户推理一些关于屏幕和应用程序功能的知识。例如,Add按钮的功能并不是立即可见的。
第一个屏幕背后的典型实现将使用数据传输对象(DTO)在后端和UI之间交换数据。UI从后端请求数据,这些数据封装在DTO中,UI将修改数据,然后将DTO发回到后端。后端将使用DTO来确定它必须对底层数据存储执行哪些CRUD操作。
第二个屏幕更明确地显示了业务流程方面正在发生的事情:用户正在选择座位类型的数量作为会议注册任务的一部分。根据用户正在执行的任务来考虑UI,可以更容易地将UI与CQRS模式实现中的写模型关联起来。UI可以向写端发送命令,这些命令是写端领域模型的一部分。在实现CQRS模式的限界上下文中,UI通常查询读端并接收DTO,然后向写端发送命令。
上图显示了一系列页面,这些页面使注册者能够完成“在会议上购买座位”的任务。在第一页,注册者选择座位的类型和数量。在第二页,注册者可以查看她所预订的座位,输入她的联系方式,并完成必要的付款信息。然后系统将注册者重定向到支付提供者,如果支付成功完成,系统将显示第三个页面。第三个页面显示了订单的摘要,并提供了到注册者可以启动其他任务的页面的链接。
为了突出显示基于任务的UI中命令和查询的角色,故意简化了上图中所示的序列。例如,实际流程包括系统根据注册者选择的支付类型显示的页面,以及如果支付失败系统显示的错误页面。
Gary(CQRS专家)发言:您并不总是需要使用基于任务的UI。在某些场景中,简单的CRUD风格的UI工作得很好。您必须评估基于任务的UI的好处是否大于所需的额外实现工作。通常,选择实现CQRS模式的限界上下文也是受益于基于任务的UI的限界上下文,因为它们具有更复杂的业务逻辑和更复杂的用户交互。
我想一劳永逸地声明,CQRS不需要基于任务的UI。我们可以将CQRS应用于基于CRUD的接口(尽管创建分离的数据模型之类的事情要困难得多)。
然而,有一件事确实需要基于任务的UI。这就是领域驱动设计。
-Greg Young, CQRS, Task Based UIs, Event Sourcing agh!
更多信息,请参见参考指南中的第4章“深入CQRS和ES”。
CRUD您不应该将CQRS模式用作顶层体系结构的一部分。您应该只在模式带来明显好处的限界上下文中实现模式。在Contoso会议管理系统中,会议管理限界上下文是整个系统中相对简单、稳定和低容量的一部分。因此,团队决定使用传统的两层CRUD风格的体系结构来实现这个限界上下文。
有关CRUD风格的体系结构何时适合(或不适合)的讨论,请参阅博客文章:Why CRUD might be what they want, but may not be what they need
限界上下文之间的集成会议管理限界上下文需要与订单和注册限界上下文集成。例如,如果业务客户更改会议管理限界上下文中座位类型的配额,则必须将此更改传播到订单和注册限界上下文中。此外,如果注册者向会议添加了一个新的参会者,业务客户必须能够在会议管理网站的列表中查看到参会者的详细信息。
从会议管理限界上下文中推送更新下面是几位开发人员和领域专家之间的对话,这些对话强调了团队在计划如何实现此集成时需要解决的一些关键问题。