Order类的抽象基类定义了Update方法。下面的代码示例显示了这个方法以及EventSourced类中的Id和Version属性。
private readonly Guid id; private int version = -1; protected EventSourced(Guid id) { this.id = id; } public int Version { get { return this.version; } } protected void Update(VersionedEvent e) { e.SourceId = this.Id; e.Version = this.version + 1; this.handlers[e.GetType()].Invoke(e); this.version = e.Version; this.pendingEvents.Add(e); }Update方法设置Id并递增聚合的版本。它还确定应该调用聚合中的哪个事件处理程序来处理事件类型。
Markus(软件开发人员)发言:每次系统更新聚合的状态时,都会增加聚合的版本号。
下面的代码示例显示Order类中的事件处理程序方法,这些方法是在调用上面显示的命令方法时调用的。
private void OnOrderPartiallyReserved(OrderPartiallyReserved e) { this.seats = e.Seats.ToList(); } private void OnOrderReservationCompleted(OrderReservationCompleted e) { this.seats = e.Seats.ToList(); } private void OnOrderExpired(OrderExpired e) { } private void OnOrderPaymentConfirmed(OrderPaymentConfirmed e) { this.isConfirmed = true; }这些方法更新聚合的状态。
聚合必须能够处理来自其他聚合的事件和它自己引发的事件。Order类中的受保护构造函数列出Order聚合可以处理的所有事件。
protected Order() { base.Handles<OrderPlaced>(this.OnOrderPlaced); base.Handles<OrderUpdated>(this.OnOrderUpdated); base.Handles<OrderPartiallyReserved>(this.OnOrderPartiallyReserved); base.Handles<OrderReservationCompleted>(this.OnOrderReservationCompleted); base.Handles<OrderExpired>(this.OnOrderExpired); base.Handles<OrderPaymentConfirmed>(this.OnOrderPaymentConfirmed); base.Handles<OrderRegistrantAssigned>(this.OnOrderRegistrantAssigned); } 事件持久化当聚合在EventSourcedAggregateRoot类的Update方法中处理事件时,它将该事件添加到挂起事件的私有列表中。此列表将在名为Events的类(是EventSourced抽象类的实现类)中暴露成IEnumerable类型的公开属性。
来自OrderCommandHandler类的以下代码示例展示了处理程序如何调用Order类中的方法来处理命令,然后使用存储库将所有挂起事件附加到存储中,从而持久存储Order聚合的当前状态。
public void Handle(MarkSeatsAsReserved command) { var order = repository.Find(command.OrderId); if (order != null) { order.MarkAsReserved(command.Expiration, command.Seats); repository.Save(order); } }下面的代码示例显示了SqlEventSourcedRepository类中Save方法的初始简单实现。
备注:这些示例引用的是一个基于SQL Server实现的事件存储。这是最初的方法,后来被基于Azure表存储的实现所取代。基于SQL server实现的事件存储仍然保留在解决方案中,这是为了方便您可以在本地运行应用程序,并使用这个实现来避免对Azure的任何依赖。 public void Save(T eventSourced) { // TODO: guarantee that only incremental versions of the event are stored var events = eventSourced.Events.ToArray(); using (var context = this.contextFactory.Invoke()) { foreach (var e in events) { using (var stream = new MemoryStream()) { this.serializer.Serialize(stream, e); var serialized = new Event { AggregateId = e.SourceId, Version = e.Version, Payload = stream.ToArray() }; context.Set<Event>().Add(serialized); } } context.SaveChanges(); } // TODO: guarantee delivery or roll back, or have a way to resume after a system crash this.eventBus.Publish(events); } 通过重播事件来重建状态当处理程序类从存储中加载聚合实例时,它通过重播存储的事件流来加载实例的状态。
Poe(IT运维人员)发言:我们后来发现,使用事件源并能够重播事件对于分析运行在云中的生产系统中的bug是非常宝贵的技术。我们可以创建事件存储的本地副本,然后在本地重播事件流,并在Visual Studio中调试应用程序,以准确理解生产系统中发生了什么。