请设计一个支持事件溯源的聚合,并针对该聚合编写单元测试,确保其在各种复杂场景下的正确性。
设计支持事件溯源的聚合及单元测试
1. 聚合设计
假设我们正在设计一个电子商务系统,其中一个核心的聚合根是Order(订单)。这个Order聚合支持事件溯源,每个操作(如创建订单、添加商品、支付订单等)都会产生一个事件,这些事件会记录下来,从而可以重放事件以重建聚合的状态。
1.1 基础结构
首先,定义一个基类或接口来表示聚合根:
public abstract class AggregateRoot
{
private List<IDomainEvent> _uncommittedChanges;
public Guid Id { get; protected set; }
protected AggregateRoot()
{
_uncommittedChanges = new List<IDomainEvent>();
}
protected void Apply(IDomainEvent @event)
{
@event.AggregateId = Id;
ApplyChange(@event, true);
}
private void ApplyChange(IDomainEvent @event, bool isNew)
{
if (isNew)
{
_uncommittedChanges.Add(@event);
}
When((dynamic)@event);
}
protected abstract void When(IDomainEvent @event);
}
1.2 具体聚合根实现
接下来,我们实现具体的聚合根Order:
public class Order : AggregateRoot
{
public Order()
{
ApplyChange(new OrderCreated(Id));
}
public List<OrderItem> Items { get; private set; } = new List<OrderItem>();
public OrderStatus Status { get; private set; } = OrderStatus.Created;
public void AddItem(OrderItem item)
{
Apply(new ItemAdded(item.ProductId, item.Quantity));
}
public void PayOrder()
{
if (Status != OrderStatus.Created)
throw new InvalidOperationException("Order must be created to be paid.");
Apply(new OrderPaid());
}
protected override void When(IDomainEvent @event)
{
switch (@event)
{
case OrderCreated e:
Status = OrderStatus.Created;
break;
case ItemAdded e:
Items.Add(new OrderItem(e.ProductId, e.Quantity));
break;
case OrderPaid e:
Status = OrderStatus.Paid;
break;
}
}
}
2. 单元测试
为了确保Order聚合在各种复杂场景下的正确性,我们需要编写一系列的单元测试。使用xUnit作为测试框架。
2.1 测试订单创建
[Fact]
public void Should_Create_Order()
{
// Arrange
var orderId = Guid.NewGuid();
var order = new Order(orderId);
// Act
var events = order.GetUncommittedChanges();
// Assert
events.Should().HaveCount(1);
events[0].Should().BeOfType<OrderCreated>();
}
2.2 测试添加商品
[Fact]
public void Should_Add_Item_To_Order()
{
// Arrange
var orderId = Guid.NewGuid();
var order = new Order(orderId);
var itemId = Guid.NewGuid();
var quantity = 2;
// Act
order.AddItem(new OrderItem(itemId, quantity));
var events = order.GetUncommittedChanges();
// Assert
events.Should().HaveCount(2);
events[1].Should().BeOfType<ItemAdded>();
((ItemAdded)events[1]).ProductId.Should().Be(itemId);
((ItemAdded)events[1]).Quantity.Should().Be(quantity);
}
2.3 测试支付订单
[Fact]
public void Should_Pay_Order()
{
// Arrange
var orderId = Guid.NewGuid();
var order = new Order(orderId);
var itemId = Guid.NewGuid();
var quantity = 2;
order.AddItem(new OrderItem(itemId, quantity));
// Act
order.PayOrder();
var events = order.GetUncommittedChanges();
// Assert
events.Should().HaveCount(3);
events[2].Should().BeOfType<OrderPaid>();
}
2.4 测试无效操作
[Fact]
public void Should_Not_Pay_Order_Twice()
{
// Arrange
var orderId = Guid.NewGuid();
var order = new Order(orderId);
var itemId = Guid.NewGuid();
var quantity = 2;
order.AddItem(new OrderItem(itemId, quantity));
order.PayOrder();
// Act & Assert
order.Invoking(x => x.PayOrder()).Should().Throw<InvalidOperationException>();
}
以上测试涵盖了订单创建、添加商品、支付订单以及无效操作的场景,确保了Order聚合在各种复杂场景下的正确性。