CQRS之旅——旅程3(订单和注册限界上下文) (5)

在DDD中,聚合表示一致性边界。因此,具有两个聚合的第一个模型,级别具有两个聚合和一个流程管理器的第三个模型将涉及两个事务:一个在系统持久化新的订单(Order)聚合时,另一个在系统持久化更新的可用座位(SeatsAvailability)聚合时。

备注:术语“一致性边界”指的是你可以假设所有元素始终保持一致的边界。

为了确保注册者创建订单时系统的一致性,两个事务都必须成功。为了保证这一点,我们必须采取步骤,通过确保基础设施可靠地向聚合传递消息,从而确保系统最终是一致的。

在第二个模型中,使用单一聚合,当注册者下订单时,我们只有一个事务。这似乎是三种模型里最简单的一种。

并发

注册过程发生在多用户环境中,许多注册者可以尝试同时购买座位。团队决定使用预约模式来解决注册过程中的并发问题。在这种情况下,这意味着为注册者最初保留了座位(然后其他注册者无法使用这些座位)。如注册者在超时时间内完成付款,系统保留预订,否则,系统将取消预订。

此预订系统引入了对附加消息类型的需求,例如,报告注册者已付款的事件,或报告超时发生的事件。

这个超时还要求系统在某个地方添加一个计时器来跟踪预订何时过期。

对这种使用消息序列和需要计时器的复杂模型,最好的办法就是使用流程管理器。

聚合和聚合根

在订单(Order)聚合和可用座位(SeatsAvailability)聚合这种两个聚合里,团队很容易识别出组成聚合的实体和聚合根。在单一聚合的模型中,选择不是很明确:通过SeatsAvailability实体访问Order,或者通过Order实体访问SeatsAvailability,这似乎都不太自然。创建作为聚合根的新实体似乎没有必要。

团队决定采用包含流程管理器的模型,因为它提供了在这个限界上下文中处理并发需求的最佳方法。



实现细节

本节介绍订单和注册限界上下文中实现的一些重要特性。您或许需要获取一份代码的拷贝,这样就可以跟随我们的脚步。您可以从Download center下载它,或者在github:mspnp/cqrs-journey-code上得到它

不要期望代码示例与参考实现中的代码完全匹配。本章描述了CQRS过程中的一个步骤,随着我们了解更多并重构代码,实现可能会发生变化。 高层架构

正如我们在上一节中描述的,团队最初决定使用CQRS模式在会议管理系统中实现预订,但不使用事件源(Event Sourcing)。下图显示了实现的关键元素:MVC web应用程序、使用Azure SQL数据库实例实现的数据存储、读写模型和一些基础设施组件。

备注:我们将在本节稍后的部分描述读写模型中发生的事情。

注册限界上下文的高层架构

下面的部分与上图中的数字相关,并提供了关于体系结构中各个元素的更多细节。

使用读模型(Read Model)查询数据

ConferenceController类包含一个名为Display的action,该action创建一个包含特定会议信息的视图(View)。这个控制器类使用以下代码从读模型里查询:
```C#
public ActionResult Display(string conferenceCode)
{
var conference = this.GetConference(conferenceCode);

return View(conference);
}

private Conference.Web.Public.Models.Conference GetConference(string conferenceCode)
{
var repo = this.repositoryFactory();
using (repo as IDisposable)
{
var conference = repo.Query().First(c => c.Code == conferenceCode);

var conferenceModel = new Conference.Web.Public.Models.Conference { Code = conference.Code, Name = conference.Name, Description = conference.Description }; return conferenceModel; }

}
```
读模型(Read Model)从数据存储中检索信息,并使用数据传输对象(DTO)将信息返回给控制器。

发出命令

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsfsgw.html