Предположим, у нас есть система, которая содержит только три агрегата Employee, Organization и QueueNode. У каждого есть свое поведение и границы, поэтому их нельзя объединить в одну совокупность.
Итак, теперь у меня есть требование
Authorized employees should be able to delete queues, keeping a log of when and who did it.
Как бы вы могли решить эту проблему? Я расскажу о своих выводах ниже.
Опция 1 Использование службы домена
class EmployeeDeleteQueueNodeService{
private QueueNodeRepository nodeRepo; // This will be injected in the constructor
void execute(Employee employee, QueueNode node){
node.raiseEvent(new QueueNodeDeleted(employee, node));
this.nodeRepo.delete(node);
}
}
А затем внедрите эту службу в службу приложения, выполняющую вариант использования по удалению QueueNode.
Вариант 2 Внедрение QueueNodeRepository в метод агрегата Employee.
class Employee{
/// Constructor, unrelated business methods
void deleteQueueNode(QueueNode node, QueueNodeRepository nodeRepository){
node.raiseEvent(new QueueNodeDeleted(this, node));
nodeRepository.delete(node);
}
}
Это создает более подробный код, который всегда лучше. Меня беспокоит только то, что я передаю репозиторий методу Employee, который, кажется, вызывает некоторые споры, лично мне нравится такой подход.
Вариант 3 То же, что и выше, но удаление выполняется после срабатывания события
class Employee{
/// Constructor, unrelated business methods
void deleteQueueNode(QueueNode node, QueueNodeRepository nodeRepository){
node.raiseEvent(new QueueNodeDeleted(this, node));
}
}
// Somewhere else in the Application layer
class QueueNodeDeletionListener {
private QueueNodeRepository nodeRepo; // This will be injected in the constructor
void notify(QueueNodeDeleted event){
this.nodeRepo.delete(event.getQueueNode());
}
}
Я думаю, что «строгий» подход DDD будет рекомендовать это, особенно с архитектурой источников событий (которую я не использую). Однако мне кажется неправильным, что метод внутри Employee вызывает событие только на другом агрегате (может быть, это совершенно неправильно, это просто мое внутреннее ощущение).
Если это правильное решение, где должен жить слушатель событий? должен ли он быть зарегистрирован на уровне приложения и уведомлен после выполнения первого варианта использования или публикация и удаление события должны выполняться в одной транзакции (прослушиватель уровня домена)? Дайте мне знать, что вы думаете.
Также я читал из нескольких источников, что обычно понятие «Удалить» не очень часто присутствует в бизнес-модели, однако в моем конкретном случае я уверен, что это так.
@ inf3rno Я понимаю и поддерживаю ваше чутье в этом отношении (я думаю, вы имели в виду первое? поскольку оно единственное без прямого взаимодействия между двумя агрегатами). Однако это делает код менее подробным, поскольку employee.deleteQueueNode(node) гораздо более понятен, чем использование this.employeeDeleteQueueNode(employee, node) из точки обзора прикладного уровня.
Да, первое. Извините, я только что проснулся. :-)
@ inf3rno Нет проблем! Ваш ответ очень ценен. Еще один действительный аргумент в пользу этого - «создание» агрегатов, которые мы используем фабрикой, которая является службой домена, поэтому имеет смысл использовать службу домена также и для удаления агрегатов.
Я не уверен, что взаимодействие пользователя с вашей системой должно быть частью вашего домена таким образом. Я имею в виду, что ваш Сотрудник является пользователем системы, у него есть привилегии, но я бы не стал делать что-то вроде employee.doThisBusinessProcess() в своем коде, потому что в этом случае все будет делать агрегат Сотрудник. Я бы просто привязал идентификатор пользователя к каждому событию автоматически. Но это опять же интуиция. Пытаюсь почитать, как в DDD проходит удаление, может что-нибудь найду.
@ inf3rno Хммм, это очень интересный вывод. Таким образом, большинство вариантов использования будут вызываться участниками (сотрудниками, клиентами, внешними службами и т. д.), Что имеет некоторый смысл, но приведет к созданию больших агрегатов (может или не может быть так, поскольку я использую ограниченный контекстах, поэтому «Сотрудник» присутствует в нескольких контекстах с разными бизнес-процессами). Спасибо за исследование.


Мое чутье - второе, поскольку теоретически агрегаты не могут касаться друг друга, поэтому один агрегат не может нести ответственность за удаление другого агрегата. Другой вариант: вы переместите QueueNode в Employee как объект, если ваш дизайн позволяет это (возможно, нет). Хотя я не разбираюсь в этой теме.