DDD - Кто несет ответственность за совокупное «удаление»?

Предположим, у нас есть система, которая содержит только три агрегата 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 вызывает событие только на другом агрегате (может быть, это совершенно неправильно, это просто мое внутреннее ощущение).

Если это правильное решение, где должен жить слушатель событий? должен ли он быть зарегистрирован на уровне приложения и уведомлен после выполнения первого варианта использования или публикация и удаление события должны выполняться в одной транзакции (прослушиватель уровня домена)? Дайте мне знать, что вы думаете.

Также я читал из нескольких источников, что обычно понятие «Удалить» не очень часто присутствует в бизнес-модели, однако в моем конкретном случае я уверен, что это так.

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

inf3rno 10.04.2021 06:31

@ inf3rno Я понимаю и поддерживаю ваше чутье в этом отношении (я думаю, вы имели в виду первое? поскольку оно единственное без прямого взаимодействия между двумя агрегатами). Однако это делает код менее подробным, поскольку employee.deleteQueueNode(node) гораздо более понятен, чем использование this.employeeDeleteQueueNode(employee, node) из точки обзора прикладного уровня.

Ahmed Wessam 10.04.2021 06:39

Да, первое. Извините, я только что проснулся. :-)

inf3rno 10.04.2021 06:40

@ inf3rno Нет проблем! Ваш ответ очень ценен. Еще один действительный аргумент в пользу этого - «создание» агрегатов, которые мы используем фабрикой, которая является службой домена, поэтому имеет смысл использовать службу домена также и для удаления агрегатов.

Ahmed Wessam 10.04.2021 06:41

Я не уверен, что взаимодействие пользователя с вашей системой должно быть частью вашего домена таким образом. Я имею в виду, что ваш Сотрудник является пользователем системы, у него есть привилегии, но я бы не стал делать что-то вроде employee.doThisBusinessProcess() в своем коде, потому что в этом случае все будет делать агрегат Сотрудник. Я бы просто привязал идентификатор пользователя к каждому событию автоматически. Но это опять же интуиция. Пытаюсь почитать, как в DDD проходит удаление, может что-нибудь найду.

inf3rno 10.04.2021 06:44

@ inf3rno Хммм, это очень интересный вывод. Таким образом, большинство вариантов использования будут вызываться участниками (сотрудниками, клиентами, внешними службами и т. д.), Что имеет некоторый смысл, но приведет к созданию больших агрегатов (может или не может быть так, поскольку я использую ограниченный контекстах, поэтому «Сотрудник» присутствует в нескольких контекстах с разными бизнес-процессами). Спасибо за исследование.

Ahmed Wessam 10.04.2021 06:52
В PHP
В PHP
В большой кодовой базе с множеством различных компонентов классы, функции и константы могут иметь одинаковые имена. Это может привести к путанице и...
Принцип подстановки Лискова
Принцип подстановки Лискова
Принцип подстановки Лискова (LSP) - это принцип объектно-ориентированного программирования, который гласит, что объекты суперкласса должны иметь...
0
6
11
0

Другие вопросы по теме

В каком шаблоне дизайна приведен ниже блок кода?
Вопрос о шаблоне проектирования C++, чистый виртуальный метод называется
Открытые / закрытые состояния меню, унаследованные в дочерних элементах со ступенчатыми переходами в попутном ветре / реакции
Как я могу одеть / раздеть (добавить / удалить свойства) экземпляр класса без упаковки в другой класс?
Как оптимизировать использование интерфейсного объекта, когда в одном случае вам нужны необязательные свойства, а в другом нет?
Должен ли я написать подкласс, перечисление или использовать атрибут?
Цепочка ответственности: есть ли способ добавить свойство в первую цепочку, чтобы включить следующий процессор (цепочку правил) для вызова?
Симпатичные шаблоны для функций
Реализует ли Django ORM шаблон Builder?
Это плохая практика - создавать ловушку, не имеющую доступа к жизненному циклу React?