CQRS之旅——旅程4(扩展和增强订单和注册限界上下文) (5)

当注册者创建一个订单并预订座位时,这些座位将保留一段固定的时间。RegistrationProcessManager实例将预订从可用座位(SeatsAvailability)聚合中转发,它将预订过期的时间传递给订单(Order)聚合。下面的代码示例显示订单(Order)聚合如何接收和存储预订过期时间。

public DateTime? ReservationExpirationDate { get; private set; } public void MarkAsReserved(DateTime expirationDate, IEnumerable<SeatQuantity> seats) { ... this.ReservationExpirationDate = expirationDate; this.Items.Clear(); this.Items.AddRange(seats.Select(seat => new OrderItem(seat.SeatType, seat.Quantity))); }

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

在Order的构造函数中,ReservationExpirationDate最初被设置为在Order实例化后的15分钟。RegistrationProcessManager类可能会根据实际预订的时间进行修改。实际时间指的是流程管理器向订单(Order)聚合发送MarkSeatsAsReserved命令的时间。

当RegistrationProcessManager将MarkSeatsAsReserved命令发送到订单(Order)聚合(携带UI将显示的过期时间)时,它还向自己发送一条命令,以启动释放预订座位的过程。这个ExpireRegistrationProcess命令在过期区间加上一个5分钟的缓冲来保存。这个缓冲是为了确保服务器之间的时间差不会导致RegistrationProcessManager类在UI中的倒计时器清零之前就释放预留的座位。下面的代码示例展示RegistrationProcessManager类,UI使用MarkSeatsAsReserved命令中的Expiration属性来显示倒计时器,而ExpireRegistrationProcess命令中的Delay属性确定何时释放保留的座位。

public void Handle(SeatsReserved message) { if (this.State == ProcessState.AwaitingReservationConfirmation) { var expirationTime = this.ReservationAutoExpiration.Value; this.State = ProcessState.ReservationConfirmationReceived; if (this.ExpirationCommandId == Guid.Empty) { var bufferTime = TimeSpan.FromMinutes(5); var expirationCommand = new ExpireRegistrationProcess { ProcessId = this.Id }; this.ExpirationCommandId = expirationCommand.Id; this.AddCommand(new Envelope<ICommand>(expirationCommand) { Delay = expirationTime.Subtract(DateTime.UtcNow).Add(bufferTime), }); } this.AddCommand(new MarkSeatsAsReserved { OrderId = this.OrderId, Seats = message.ReservationDetails.ToList(), Expiration = expirationTime, }); } ... }

MVC项目中的RegistrationController类在读取端检索订单信息。DraftOrder类包含控制器使用ViewBag类传递给视图的预约过期时间,如下面的代码示例所示。

[HttpGet] public ActionResult SpecifyRegistrantDetails(string conferenceCode, Guid orderId) { var repo = this.repositoryFactory(); using (repo as IDisposable) { var draftOrder = repo.Find<DraftOrder>(orderId); var conference = repo.Query<Conference>() .Where(c => c.Code == conferenceCode) .FirstOrDefault(); this.ViewBag.ConferenceName = conference.Name; this.ViewBag.ConferenceCode = conference.Code; this.ViewBag.ExpirationDateUTCMilliseconds = draftOrder.BookingExpirationDate.HasValue ? ((draftOrder.BookingExpirationDate.Value.Ticks - EpochTicks) / 10000L) : 0L; this.ViewBag.OrderId = orderId; return View(new AssignRegistrantDetails { OrderId = orderId }); } }

然后MVC的视图使用JavaScript显示动画倒计时器。

使用ASP.NET MVC validation来验证命令

您应该确保应用程序中的MVC控制器发送给写模型的任何命令都将成功。在将命令发送到写模型之前,可以使用MVC中的特性在客户端和服务器端验证命令。

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

客户端验证对用户来说主要是比较方便,因为它不用往返于服务器就可以帮助用户正确完成表单填写。但您仍然需要实现服务器端验证,以确保在将数据转发到写模型之前对其进行过验证。

下面的代码示例显示了AssignRegistrantDetails命令类,它使用DataAnnotations指定验证需求;在本例中,要求FirstName、LastName和Email字段不为空。

using System; using System.ComponentModel.DataAnnotations; using Common; public class AssignRegistrantDetails : ICommand { public AssignRegistrantDetails() { this.Id = Guid.NewGuid(); } public Guid Id { get; private set; } public Guid OrderId { get; set; } [Required(AllowEmptyStrings = false)] public string FirstName { get; set; } [Required(AllowEmptyStrings = false)] public string LastName { get; set; } [Required(AllowEmptyStrings = false)] public string Email { get; set; } }

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

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