"Удаление" агрегата в DDD?

Разбираюсь с DDD и не могу понять, кто производит действия над агрегатами. В частности, например, действие отражающие "удаление" агрегата.
Например, существует агрегат Каталог товаров (Catalog) с корневой сущностью Catalog и объектами-значениями CatalogID, Name.
Есть хранилище, используя которое можем вытащить агрегат, изменить его и сохранить обратно

$catalog = $repository->find($catalogId);
$catalog->rename('Some another name')
$repository->update($catalog);


Также в рамках другого ограниченного контекста, существуют агрегат Интеграция (Integration), с корневой сущностью Integration, объектами значениями IntegrationId, Name и коллекцией объектов значений EntityRelations. С помощью этого агрегата у нас есть возможность связать нашу любую сущность из предметной области с сущностью стороннего интегрируемого сервиса (ERP, 1C)

...
$integration->addRelation(new EntityRelation('catalog', $catalog->id(), $externalCatalogId));
$integrationRepository->update($integration);


И теперь собственно вопрос, как мне "удалить" каталог таким образом, чтобы он "удалился" вместе со всеми интеграционными связями? Очевидно, можно ввести событие "CatalogDeleted", подписаться на него и "удалять" связанные с этим каталогом интеграционные связи. Но как Catalog узнает, что он "удаляется"?

Есть идея внутри сущности Catalog объявить защищенный метод onDelete(), и в хранилище через рефлексию его дергать.
class Catalog extends AbstractEntity
{
   ...
   protected function onDelete()
   {
       $eventManager->dispatch(new CatalogDeleted($this->id()));
   }
   ...
}

class CatalogRepository extends AbstractRepository implements CatalogRepositoryInterface
{
   ...
   public function delete(Catalog $catalog)
   {
        $method = new \ReflectionMethod($catalog, 'onDelete');
        $method->setAccessible(true);
        $method->invoke($catalog);
        //дальше удаляем из БД
   }
   ...
}


Можно ли считать такой подход нормальным?
  • Вопрос задан
  • 484 просмотра
Пригласить эксперта
Ответы на вопрос 1
@xfg
Идея у вас верная, только вместо защищенного onDelete, делают обычный публичный метод remove, который ничего не делает, а просто выбрасывает доменное событие. Этот метод как и все остальные просто вызывается внутри application сервиса, а затем удаляется из репозитория.


$catalog->remove();
$repository->remove($catalog);


Чтобы была атомарность, нужно события выбрасывать после того, как изменения были сделаны в базе данных. Само событие нужно также в базу сохранять, в одной транзакции с созданием/обновлением/удалением данных об агрегате. Какая-то штука затем должна удалять обработанные события. Какая-то штука должна проталкивать события из базы, которые почему-то там уж слишком долго живут - подписчикам. Обычно происходит при сбое, когда в базу событие записалось, а подписчикам не отправилось.

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

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

Войдите, чтобы написать ответ

Похожие вопросы