使用领域事件更新数据库中的实体

Using Domain Events to Update Entity in Database

我有一个 PurchaseOrder 聚合根,它有两个方法 FinalizeOrder 和 CancellOrder,它们都记录事件:OrderFinaziled 和 OrderCancelled。我被困在建模订单存储库上,我可以使用存储库模式中的那些事件来更新数据库中的实体吗?我不会在每次更改后都保存整个聚合根,我只想保存更改的字段,我使用的是 SqlClient,没有 ORM。

我的聚合根基 class:

public class AggregateRootBase<TID> : EntityBase<TID>
{
    public AggregateRootBase(TID id) : base(id)
    {
    }

    private readonly List<IDomainEvent> recordedEvents = new List<IDomainEvent>();

    public IEnumerable<IDomainEvent> GetEvents()
    {
        return recordedEvents;
    }
    public void MarkEventsAsProcessed()
    {
        recordedEvents.Clear();
    }

    protected void RecordEvent(IDomainEvent @event)
    {
        recordedEvents.Add(@event);
    }
}

PurchaseOrder class(跳过大部分属性):

public class PurchaseOrder : AggregateRootBase<int>
{
   public PurchaseOrder(int id) : base(id)
   {
      IsFinalized = false;
      IsCancelled = false;
   }
   public bool IsFinalized { get; set; }
   public bool IsCancelled { get; set; }

   public void FinalizeOrder()
   {
      IsFinalized = true;
      RecordEvent(new OrderFinalized(Id,IsFinalized));
   }
   public void CancellOrder()
   {
      IsCancelled = true;
      RecordEvent(new OrderCancelled(Id,IsCancelled));
   }
}

和存储库:

public class PurchaseOrderRepository 
{
   void Save(PurchaseOrder purchaseOrder)
   {
      var events = purchaseOrder.GetEvents();
      foreach (var evt in events)
      {
         if(evt.GetType() == typeof(OrderFinalized))
            // use event args and update field using SqlCommand
         else if (evt.GetType() == typeof(OrderCancelled))
            // use event args and Update field Using SqlCommand
      }
   }
}

我还有一个 EventDispatcher,它在 AggregateRoot 成功持久化后调度事件(电子邮件通知)。

如果您想使用域事件来保存您的更改,您正在谈论 投影 事件到您的聚合状态。有一些工具可以帮助解决这个问题。您尝试做的事情类似于模式匹配,这是大多数函数式语言的常用功能。为了让生活更轻松,您可能需要检查 Projac. We use it for projections and it works very nicely. It also has SQL Server specific implementation.

之类的内容

您可以在此处找到一个示例:

public class PortfolioProjection : SqlProjection
{
  public PortfolioProjection()
  {
    When<PortfolioAdded>(@event =>
      TSql.NonQueryStatement(
        "INSERT INTO [Portfolio] (Id, Name) VALUES (@P1, @P2)",
        new { P1 = TSql.Int(@event.Id), P2 = TSql.NVarChar(@event.Name, 40) }
    ));

    When<PortfolioRemoved>(@event =>
      TSql.NonQueryStatement(
        "DELETE FROM [Portfolio] WHERE Id = @P1",
        new { P1 = TSql.Int(@event.Id) }
    ));

    When<PortfolioRenamed>(@event =>
      TSql.NonQueryStatement(
        "UPDATE [Portfolio] SET Name = @P2 WHERE Id = @P1",
        new { P1 = TSql.Int(@event.Id), P2 = TSql.NVarChar(@event.Name, 40) }
    ));
  }
}

然后你可以初始化投影仪实例:

_projector = new SqlProjector(
   Resolve.WhenEqualToHandlerMessageType(new PortfolioProjection()),
   new TransactionalSqlCommandExecutor(
     new ConnectionStringSettings(
       "projac",
        @"Data Source=(localdb)\ProjectsV12;Initial Catalog=ProjacUsage;Integrated Security=SSPI;",
        "System.Data.SqlClient"),
      IsolationLevel.ReadCommitted));

然后是项目活动:

void Save(PurchaseOrder purchaseOrder) =>
    _projector.Project(purchaseOrder.GetEvents());

您也可以检查事件溯源模式,尽管它增加了相当多的复杂性。