DDD и транзакции
DDD и транзакции
Уважаемые члены сообщества, помогите внести ясность в вопрос.
Имею шину команд. Нужно обеспечить транзакционность. Видел решения https://php-and-symfony.matthiasnoback. ... mmand-bus/ связанные и использованием перехватов, шина декорируется классом, где разруливается транзакция. Т.е. обработчики команд ничего не знают о транзакции.
Таким образом, если написать обработчик, который будет работать с БД, а еще отправлять письмо (делать что-то нетранзакционное) прямо или косвенно (через события), то возникает окно для несогласованности - письмо отправлено, транзакция откатилась.
Будет ли перенос управления транзакцией в обработчик команды, более правильным решением в данном случае? Не является ли вынос управления транзакцией в декоратор плохой практикой? Или все совсем наоборот и транзация является сквозным аспектом приложения и ей именно так и нужно управлять? Лично мне думается, что в этом случае появляется тесная связь между декоратором и обработчиками, использующими БД.
P.S. Полгода не заходил на форум..Очень радует, что появилось большое количество обсуждений архитектуры приложений и DDD. Сообщество растет качественно). Может и фреймворк в ответ на меняющиеся условия станет менее монолитным?) (риторический вопрос) Уж не осудите за оффтоп).
Имею шину команд. Нужно обеспечить транзакционность. Видел решения https://php-and-symfony.matthiasnoback. ... mmand-bus/ связанные и использованием перехватов, шина декорируется классом, где разруливается транзакция. Т.е. обработчики команд ничего не знают о транзакции.
Таким образом, если написать обработчик, который будет работать с БД, а еще отправлять письмо (делать что-то нетранзакционное) прямо или косвенно (через события), то возникает окно для несогласованности - письмо отправлено, транзакция откатилась.
Будет ли перенос управления транзакцией в обработчик команды, более правильным решением в данном случае? Не является ли вынос управления транзакцией в декоратор плохой практикой? Или все совсем наоборот и транзация является сквозным аспектом приложения и ей именно так и нужно управлять? Лично мне думается, что в этом случае появляется тесная связь между декоратором и обработчиками, использующими БД.
P.S. Полгода не заходил на форум..Очень радует, что появилось большое количество обсуждений архитектуры приложений и DDD. Сообщество растет качественно). Может и фреймворк в ответ на меняющиеся условия станет менее монолитным?) (риторический вопрос) Уж не осудите за оффтоп).
- samdark
- Администратор
- Сообщения: 9489
- Зарегистрирован: 2009.04.02, 13:46
- Откуда: Воронеж
- Контактная информация:
Re: DDD и транзакции
А почему не отправлять письмо только в случае успешного завершения транзакции?
Нравится Yii? Давайте сделаем его лучше!.
- samdark
- Администратор
- Сообщения: 9489
- Зарегистрирован: 2009.04.02, 13:46
- Откуда: Воронеж
- Контактная информация:
Re: DDD и транзакции
В хендлере нельзя триггернуть событие вроде "успех" и на него зацепиться чем-то ещё?
Нравится Yii? Давайте сделаем его лучше!.
Re: DDD и транзакции
В таких случаях декорирую и EventDispatcher, чтобы его метод dispatch в приватный массив все события сохранял. И после коммита в шине вызываю запуск обработки.
Re: DDD и транзакции
Т.е. декторатор шины команд, в котором обрабатывается транзакция, управляет шиной событий, верно?
Получается когда ввожу транзакции в приложение, должен буду сделать два декоратора - на шину команд и шину событий и из первого управлять вторым. В командах ничего нетранзакционного напрямую получается делать нельзя, только через шину событий. Все равно какая-то связь в виде умолчания (то, что нельзя ничего нетранзакционного делать в обработчике) остается. Компромиссное решение.
Не лучше ли управлять транзакциями в обработчике команды? Чем это хуже перехвата?
Re: DDD и транзакции
Верно:
Код: Выделить всё
class TransactionalCommandBus implements CommandBusInterface
{
public function construct(
CommandBusInterface $next,
DeferredEventDispatcher $dispatcher,
Transtaction $transaction
) { ... }
public function handle($command) {
$this->transaction->execute(function () use ($command) {
call_user_func($this->next, $command);
// $this->em->flush();
});
$this->dispatcher->handleDeferredEvents();
}
}
Управляйте:
Код: Выделить всё
public function handle(MyCommand $command)
{
$entity1 = new Entity1(...);
$entity2 = new Entity2(...);
$this->transactionManager->execute(function () use ($entity1, $entity2) {
$this->repository1->add($entity1);
$this->repository2->add($entity2);
});
$this->eventDispatcher->dispatch(...);
}
- можно явно запускать нетранзакционные операции (хотя им всё равно, в сервисе их запускают или по событию).
Хуже тем, что:
- в сервис просочилось знание о транзакциях;
- теперь нужно запускать UnitOfWork::flush() в каждом репозитории, а не один раз с транзакцией в шине.
Последний раз редактировалось ElisDN 2017.04.26, 17:10, всего редактировалось 6 раз.
- samdark
- Администратор
- Сообщения: 9489
- Зарегистрирован: 2009.04.02, 13:46
- Откуда: Воронеж
- Контактная информация:
Re: DDD и транзакции
Кажется, пример плохой... Событие на отправку почты мы триггерим только в том случае, если успешно отработала первая часть и не триггерим в противном случае. В нашем случае фейл отправки почты не означает отката изменений базы, что значит что по сути это не транзакция, а последовательность действий.Так и делаю, но проблему транзакции это не решает, Обработчик события, который будет отправлять почту, тем более не знает, завершилась транзакция или нет.
Нравится Yii? Давайте сделаем его лучше!.
Re: DDD и транзакции
Отправку почты взял чтобы не было ни у кого желания предлагать обработать нетранзакционную операцию, например сохранение файла куда-то.samdark писал(а): ↑2017.01.16, 11:10Кажется, пример плохой... Событие на отправку почты мы триггерим только в том случае, если успешно отработала первая часть и не триггерим в противном случае. В нашем случае фейл отправки почты не означает отката изменений базы, что значит что по сути это не транзакция, а последовательность действий.Так и делаю, но проблему транзакции это не решает, Обработчик события, который будет отправлять почту, тем более не знает, завершилась транзакция или нет.
Re: DDD и транзакции
Тут еще устраняется тесная связь между декоратором, в котором управляется транзакция и обработчиком, разве нет?
Например, когда я пишу обработчик, я же не знаю, что у меня там за шина будет. Обрабатывает она транзакции или нет. Если буду опираться на то, что обрабатывает - получу тесную связь, не так ли?
- samdark
- Администратор
- Сообщения: 9489
- Зарегистрирован: 2009.04.02, 13:46
- Откуда: Воронеж
- Контактная информация:
Re: DDD и транзакции
Так отправка почты и есть нетранзакционная операция...Отправку почты взял чтобы не было ни у кого желания предлагать обработать нетранзакционную операцию, например сохранение файла куда-то.
Нравится Yii? Давайте сделаем его лучше!.
Re: DDD и транзакции
Вопрос как раз в том, как правильно соблюсти эту последовательность при использовании шины команд с транзакциями.samdark писал(а): ↑2017.01.16, 11:10Кажется, пример плохой... Событие на отправку почты мы триггерим только в том случае, если успешно отработала первая часть и не триггерим в противном случае. В нашем случае фейл отправки почты не означает отката изменений базы, что значит что по сути это не транзакция, а последовательность действий.Так и делаю, но проблему транзакции это не решает, Обработчик события, который будет отправлять почту, тем более не знает, завершилась транзакция или нет.
- samdark
- Администратор
- Сообщения: 9489
- Зарегистрирован: 2009.04.02, 13:46
- Откуда: Воронеж
- Контактная информация:
Re: DDD и транзакции
Нельзя для примера с транзакциями рассматривать заведомо не транзакционные части операции, такие как отсылка почты. Это путает. Чтобы у нас вышла нормальная транзакция придётся реализовать что-то типа two-phase commit protocol на уровне шины. А для этого отсылку почты (или как минимум её постановку в очередь) нужно иметь возможность откатить.
Нравится Yii? Давайте сделаем его лучше!.
Re: DDD и транзакции
Я знаю что такое 2PC. Мне нужно было узнать, как правильно работать на шине с транзакциями с учетом того, что в обработчиках могут быть заведомо нетранзакционные операции.
Вы хотите сказать, что шину надо либо делать полностью транзакционной (все хендлеры можно откатить), либо вообще не рулить транзакциями в декораторе шины?
Вы хотите сказать, что шину надо либо делать полностью транзакционной (все хендлеры можно откатить), либо вообще не рулить транзакциями в декораторе шины?
Re: DDD и транзакции
да никак. Нельзя работать с нетранзакционными операциями без знания ими о транзакциях. Поэтому транзакции делают уровнем ниже - в репозиториях - транзакционно сохраняя агрегат в Repository::save(...). Оборачивание же шины в транзакцию некорректно, т.к. внутри хэндлера происходят действия разных слоев.
Re: DDD и транзакции
Про правило одного агрегата слышал. А как же быть со всякими групповыми операциями, охватывающими более одного агрегата? Например, сразу отменить 10 заказов - пользователь отмечает галочками и жмакает на кнопку.