【问题标题】:Using Domain Events to Update Entity in Database使用领域事件更新数据库中的实体
【发布时间】:2016-08-09 11:25:11
【问题描述】:

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

我的聚合根基类:

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 类(跳过大多数属性):

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 成功持久化后调度事件(电子邮件通知)。

【问题讨论】:

    标签: c# events repository domain-driven-design aggregateroot


    【解决方案1】:

    如果您想使用域事件来保存您的更改,您正在谈论将事件投影到您的聚合状态。有一些工具可以帮助解决这个问题。您尝试做的类似于模式匹配,这是大多数函数式语言的常见特征。为了让生活更轻松,您可能需要检查Projac 之类的内容。我们用它来做投影,效果很好。它还有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());
    

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

    【讨论】:

    • 看起来很有趣,谢谢,我去看看。
    • 关于事件溯源和预测的一个问题,我是否正确理解如果我有事件存储并且当我从事件重构域模型时它是投影,以及当我使用事件更新数据库中的聚合状态时它被称为投影,我是 DDD 和 CQRS 的新手,如果我误解了某些东西,如果不难的话,你能把它弄清楚吗?
    • 您不必使用事件溯源来使用 Projac。您只需为您的事件执行预测。
    • 我添加了更多代码,但您可以更好地查看存储库,那里有很多示例。
    • 谢谢,是的,我正在审查它;)根据您的经验,是否有更好的做法可以在不使用 ORM 框架的情况下保持聚合根更改?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多