В какой момент агрегаторы освобождают события?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Как агрегаторы освобождают события в Domain-Driven Design (DDD)
В контексте Domain-Driven Design (DDD), агрегаторы освобождают (публикуют) события предметной области (domain events) в определённый момент жизненного цикла агрегата, чтобы уведомить систему о произошедших значимых изменениях. Это ключевой механизм для обеспечения реактивности, поддержания консистентности между ограниченными контекстами и реализации архитектурных стилей, таких как Event-Driven Architecture (EDA).
Ключевой момент публикации событий
Агрегаторы освобождают события непосредственно после успешного выполнения бизнес-операции внутри агрегата, но до сохранения изменений в базе данных. Точнее, это происходит в момент, когда команда (command) успешно обработана, состояние агрегата изменилось, и события сгенерированы. Однако сами события не отправляются немедленно в очередь сообщений или внешним подписчикам — они собираются и временно хранятся внутри агрегата (обычно в коллекции).
Типичный поток освобождения событий:
- Генерация события внутри метода агрегата при изменении состояния.
- Хранение события в приватном списке (например,
private $events = []). - Извлечение событий фреймворком или инфраструктурным слоем после успешного сохранения агрегата.
- Публикация событий в шину событий (event bus) или диспетчер событий.
Пример кода на PHP
<?php
class Order extends AggregateRoot
{
private array $events = [];
private OrderStatus $status;
private OrderId $id;
public function cancel(string $reason): void
{
// Бизнес-логика
if ($this->status->isCancelled()) {
throw new OrderAlreadyCancelledException();
}
$this->status = OrderStatus::cancelled();
// Генерация события
$this->recordEvent(new OrderCancelled(
$this->id,
$reason,
new DateTimeImmutable()
));
}
private function recordEvent(DomainEvent $event): void
{
$this->events[] = $event;
}
public function releaseEvents(): array
{
$events = $this->events;
$this->events = [];
return $events;
}
}
<?php
// Инфраструктурный слой (например, в UnitOfWork или репозитории)
class DoctrineOrderRepository implements OrderRepository
{
public function save(Order $order): void
{
// 1. Сохраняем агрегат в БД
$this->entityManager->persist($order);
$this->entityManager->flush();
// 2. Извлекаем и публикуем события
$events = $order->releaseEvents();
foreach ($events as $event) {
$this->eventBus->dispatch($event);
}
}
}
Почему именно такая последовательность?
-
Транзакционная целостность: События публикуются только после успешного сохранения агрегата. Если транзакция откатывается, события не будут отправлены, что предотвращает несогласованность данных.
-
Атомарность: Изменение состояния агрегата и генерация событий происходят как единая атомарная операция. События — неотъемлемая часть изменения состояния.
-
Изоляция агрегата: Агрегат не должен зависеть от инфраструктуры публикации событий. Он только генерирует события, а их диспетчеризацией занимается внешний механизм.
Важные нюансы реализации
- Паттерн "Collect-and-Dispatch": Агрегат накапливает события, а инфраструктура извлекает и рассылает их после коммита транзакции.
- Механизм извлечения: Метод
releaseEvents()должен очищать внутреннюю коллекцию после извлечения, чтобы избежать повторной публикации тех же событий. - Обработка в рамках транзакции: В распределённых системах часто используется паттерн Transactional Outbox для гарантированной доставки событий, где события сначала сохраняются в ту же транзакцию БД, а затем фоновый процесс отправляет их в брокер сообщений.
Последствия выбора момента публикации
Если события публиковать до сохранения агрегата:
- Риск несогласованности: события будут обработаны, но изменение агрегата может не сохраниться.
- Нарушение атомарности.
Если события публиковать значительно позже:
- Задержка реакции системы на изменения.
- Потенциальные проблемы с временными метками и порядком событий.
Таким образом, оптимальный момент — сразу после успешного сохранения агрегата, что балансирует между транзакционной безопасностью и своевременностью реакций системы. Это позволяет строить отказоустойчивые, слабосвязанные системы, где различные компоненты могут реагировать на изменения предметной области без прямых зависимостей друг от друга.