如何在DDD中处理事务

时间:2018-11-05 16:52:21

标签: architecture transactions domain-driven-design software-design unit-of-work

在DDD的上下文中,当您处理域事件时,交易应从何处开始和结束?

Infrastructure.Layer具有UoW实施

/// => configured via DI as Per Request
class UnitOfWork : IUnitOfWork
{
     ITransaction _transaction;
     static ISessionFactory _factory; 
     ISession _session

     UnitOfWork()
     {
         _session = _factory.OpenSession();
         _transaction = _session.BeginTransaction();  ///=> start transaction 
     }

     void Commit()
     {
          try
             _transaction.Commit();
          catch
            _transaction.Rollback(); 
          finally
            Dispose();
     } 
}

Application.Layer UseCase处理程序

class SomeAppServiceUseCaseHandler : IUseCaseHandler
{
      IUnitOfWork _uow;
      ISomeRepo _repo;

      AppService(IUnitOfWork uow, ISomeRepo repo)
      {
          _uow = uow;
          _repo = repo;
      }

      void UseCaseHandler(Request request)
      {

         SomeAggregate agg = _repo.GetAggregate(request.Id) 

                       agg.DoSomethingToChangeState();

         _repo.UpdateAgg(agg);

         _uow.Commit(agg);  ///=> commit changes for this transaction success
      }
}

,并且在Domain.Layer中具有该方法,该方法还将向域事件列表中添加用于聚合的Domain.Event。

SomeAggregate : AggregateRoot
{
   DoSomethingToChangeState()
   {
       .... do something

       var someObject;
       base.AddEvent(new SomethingHappenedEvent(someObject)));
   }
}

Application.Layer具有Domain.Event处理程序

class SomethingHappenedEventHander : Handler<SomethingHappenedEvent>
{
    IRepo repo;
    IUnitOfWork _uow;

    DomainEventHander(IRepo repo, IUnitOfWork uow)
    {
        _repo = repo;
        _uow= uow;
    }

    HandleEvent(someObject)
    {
         AnotherAggregate agg = new AnotherAggregate ();
                          agg.DoSomeCommand(someObject);

         _repo.Create(agg);
         _uow.Commit();  ///=> commit changes for same transaction fail, should rollback prev transaction as well
    }
}

我觉得这不对

  1. 谁应该发布事件?从我看来,UoW应该在Commit()方法中这样做,但是我认为这是不对的,我认为UoW不应该这样做,但是我看不到还有谁可以。

  2. 如果在链中的某个点失败了,那么我已经提交了一些数据,如果一路失败了,我很可能不想这样做。

那么应该如何正确处理这两种情况?

1 个答案:

答案 0 :(得分:1)

正如康斯坦丁所说,每个命令只能更新一个聚合。为什么?因为通过更新事务中的多个聚合,会降低系统的吞吐量。数据库事务边界越大,在写入数据时面临争用的可能性就越大,因此您希望保持事务的粒度尽可能细。

关于您的问题:

  1. UnitOfWork当然可以保存更改。另外,您的Repo类也可以使用新方法IRepo.Save(Aggregate)进行相同操作:

    _repo.Save(agg);
    
  2. 您的关注点是有效的。您可以(可能应该)将UnitOfWork移出Handler / UseCaseHandler级别的范围,这样您就不必具有在每个处理程序中提交UoW的样板代码。然后,您可以将两个聚合保存在一个数据库事务中。更好的方法是采用允许您从故障中恢复的体系结构。您可以:

    1. 处理命令并在事件存储中生成事件。 (UoW会这样做)
    2. 轮询事件存储中的新事件,并将其发布到队列中。
    3. 从队列中读取事件,并将其分派给已注册的任何Handler。
    4. 将新事件保存在事件存储中(再次使用UoW),该过程将重复进行。

    如果以上任何步骤失败,那么处理将从那时开始,并确保已完成工作。使用这种方法,您的应用程序更具弹性,并且可以正确地应用事务边界。