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

系统创建一个新的RegistrationProcessManager实例来管理新订单。下面来自RegistrationProcessManager类的代码示例展示了流程管理器如何处理事件。

public void Handle(OrderPlaced message) { if (this.State == ProcessState.NotStarted) { this.OrderId = message.OrderId; this.ReservationId = Guid.NewGuid(); this.State = ProcessState.AwaitingReservationConfirmation; this.AddCommand( new MakeSeatReservation { ConferenceId = message.ConferenceId, ReservationId = this.ReservationId, NumberOfSeats = message.Items.Sum(x => x.Quantity) }); } else { throw new InvalidOperationException(); } }

代码示例显示流程管理器如何更改其状态,并发送一个由SeatsAvailability聚合处理的新的MakeSeatReservation命令。代码示例还演示了如何将流程管理器实现为接收消息、更改其状态并发送新消息的状态机。

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

Markus(软件开发人员)发言:

注意我们生成一个新的全局惟一标识符(GUID)来标识新的预订。我们使用这些Guid将消息关联到正确的流程管理器实例和聚合实例。

当SeatsAvailability聚合接收到MakeReservation命令时,如果有足够的可用座位,它将进行预订。下面的代码示例显示了SeatsAvailability类如何根据是否有足够的座位引发不同的事件。

public void MakeReservation(Guid reservationId, int numberOfSeats) { if (numberOfSeats > this.RemainingSeats) { this.events.Add(new ReservationRejected { ReservationId = reservationId, ConferenceId = this.Id }); } else { this.PendingReservations.Add(new Reservation(reservationId, numberOfSeats)); this.RemainingSeats -= numberOfSeats; this.events.Add(new ReservationAccepted { ReservationId = reservationId, ConferenceId = this.Id }); } }

流程管理器RegistrationProcessManager类处理预订的接收和拒绝事件。这是一个临时的座位预订,让用户有机会进行支付。流程管理器在购买完成或预订超时过期时释放预订。下面的代码示例显示流程管理器如何处理这两种事件。

public void Handle(ReservationAccepted message) { if (this.State == ProcessState.AwaitingReservationConfirmation) { this.State = ProcessState.AwaitingPayment; this.AddCommand(new MarkOrderAsBooked { OrderId = this.OrderId }); this.commands.Add( new Envelope<ICommand>(new ExpireOrder { OrderId = this.OrderId, ConferenceId = message.ConferenceId }) { Delay = TimeSpan.FromMinutes(15), }); } else { throw new InvalidOperationException(); } } public void Handle(ReservationRejected message) { if (this.State == ProcessState.AwaitingReservationConfirmation) { this.State = ProcessState.Completed; this.AddCommand(new RejectOrder { OrderId = this.OrderId }); } else { throw new InvalidOperationException(); } }

如果预订被接受,流程管理器将通过向自身发送ExpireOrder命令启动计时器,并向订单(Order)聚合发送MarkOrderAsBooked命令。否则,它将向订单(Order)聚合发送一条ReservationRejected消息。

前面的代码示例显示了流程管理器如何发送ExpireOrder命令。基础设施负责将消息保存在队列中,等待15分钟的延迟。

您可以借鉴SeatsAvailability和RegistrationProcessManager类里的代码,以查看其他消息处理程序是如何实现的。它们都遵循相同的模式:接收消息、执行一些逻辑并发送消息。

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

Jana(软件架构师)发言:

本章展示的代码示例都来自会议管理系统的早期版本。下一章将展示当团队持续探索该领域以及学习了更多CQRS模式的知识之后,设计和实现是如何随之发展的。

基础设施

下面的序列图展示了基础设施组件如何与领域对象交互消息的。

当UI中的MVC控制器使用命令总线发送消息时,典型的交互就开始了。消息发送方异步调用命令总线上的Send方法。然后命令总线存储消息,直到消息接收者收到消息并将其转发给适当的处理程序。系统包含许多命令处理程序,这些命令处理程序向命令总线注册,以处理特定类型的命令。例如,OrderCommandHandler类为RegisterToConference、Markorderasbooking和RejectOrder命令定义了处理程序方法。下面的代码示例显示了Markorderasbooking命令的处理程序方法。处理程序方法负责寻找正确的聚合实例,调用该实例上的方法,然后保存该实例。

public void Handle(MarkOrderAsBooked command) { var repository = this.repositoryFactory(); using (repository as IDisposable) { var order = repository.Find<Order>(command.OrderId); if (order != null) { order.MarkAsBooked(); repository.Save(order); } } }

实现IRepository接口的类负责在事件总线上持久化聚合对象并发布聚合里引发的任何事件,所有的这些都是事务的一部分。

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

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