Как следует обрабатывать транзакции в контексте DDD - PullRequest
0 голосов
/ 05 ноября 2018

В контексте DDD, где должна начинаться и заканчиваться транзакция, когда вы имеете дело с событиями домена?

Инфраструктура. Слой имеет реализацию 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 Handler

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 голосов
/ 05 ноября 2018

Как констатировал Константин, для каждой команды должен обновляться только один Агрегат. Зачем? Потому что, обновляя более одного агрегата в транзакции, вы снижаете пропускную способность системы. Чем больше границы транзакций вашей базы данных, тем больше вероятность того, что вы столкнетесь с конфликтом при записи данных, поэтому вы хотите, чтобы ваши транзакции были как можно более детализированы.

По вашим вопросам:

  1. UnitOfWork, безусловно, может сохранить изменения. Кроме того, ваш класс Repo может сделать то же самое с новым методом, IRepo.Save (Aggregate):

    _repo.Save(agg);
    
  2. Ваша проблема действительна. Вы можете (и, возможно, должны) переместить свой UnitOfWork из области уровня Handler / UseCaseHandler, чтобы у вас не было этого стандартного кода для фиксации UoW в каждом обработчике. Затем вы можете сохранить оба агрегата в одной транзакции базы данных. Лучшим подходом было бы использовать архитектуру, которая позволяет восстанавливаться после сбоев. Вы могли бы:

    1. Обрабатывать команду и генерировать события в хранилище событий. (UoW сделает это)
    2. Опрос новых событий в хранилище событий и публикация их в очереди.
    3. Чтение событий из очереди и отправка их любому зарегистрированному обработчику.
    4. Сохраните новые события в хранилище событий (снова с UoW), и процесс будет повторяться.

    Если какой-либо из вышеперечисленных шагов завершится неудачно, обработка начнется с этой точки и обеспечит выполнение работы. При таком подходе ваше приложение становится более устойчивым, а границы транзакций применяются правильно.

...