Подтвердить что ты не робот

Разработка домена и внедрение IoC/зависимостей

Я пытаюсь применить теперь то, что я узнал о DDD, и я немного запутался в потоке зависимостей в модели домена.

Мои вопросы:

  • Должен ли объект знать о фабриках, репозиториях, службах в домене?
  • Должен ли репозиторий знать о службах в домене?

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

Скажем, я разрабатываю простую CMS. В CMS у меня есть коллекция Entity Entity и тегов, которая содержит объекты тэгов.

Теперь, если я хочу добавить отношение с новым тегом. Какой был бы лучший способ сделать это? (Пример в PHP)

$article->tags->add(TagEntity);
$articleRepository->save($article);

или я могу сделать это с помощью службы.

$articleService->addTag($article, TagEntity);

Как вы думаете?

Спасибо.

4b9b3361

Ответ 1

Объекты и объекты ценности никогда не должны зависеть ни от чего, кроме друг от друга. Это самые фундаментальные из всех строительных блоков DDD. Они представляют собой концепции вашей проблемной области и поэтому должны сосредоточиться на проблеме. Заставляя их зависеть от фабрик, хранилищ и служб, вы делаете это размытие фокуса. Существует еще одна проблема с ссылкой на Службы в объектах и ​​объектах Value. Поскольку службы также обладают логикой домена, у вас возникнет соблазн делегировать некоторые из обязанностей модели домена на службы, которые в конечном итоге могут привести к Модель анемичного домена.

Заводы и Хранилища - это просто помощники, используемые для создания и сохранения сущностей. В большинстве случаев они просто не имеют аналогии в реальном проблемном домене, поэтому наличие ссылок на "Фабрики и хранилища" на "Службы" и "Объекты" не имеет смысла в соответствии с логикой домена.

Что касается примера, который вы указали, так я его реализую

$article->addTag($tag);
$articleRepository->save($article);

Я бы не дал прямой доступ к базовой коллекции, потому что я мог бы хотеть, чтобы Article выполнял некоторую логику домена (наложение ограничений, проверку) на Tag, прежде чем добавлять ее в коллекцию.

Избегайте этого

$articleService->addTag($article, $tag);

Использовать службы только для выполнения операций, которые не принадлежат ни одному объекту концептуально. Сначала попробуйте подогнать его к Entity, убедитесь, что он не подходит. И только тогда используйте Сервис для него. Таким образом, вы не получите анонимных моделей домена.

ОБНОВЛЕНИЕ 1

Цитата из книги Эрика Эванса "Проект, управляемый доменами: проблема сложности в сердце программного обеспечения":

УСЛУГИ следует использовать разумно и не разрешать ОБЪЕКТЫ И ЦЕННЫЕ ОБЪЕКТЫ всех их действий.

ОБНОВЛЕНИЕ 2

Кто-то отказался от этого ответа, и я не знаю, почему. Я могу только подозревать причину. Это могут быть ссылки между Entities and Services или это может быть пример кода. Ну, я не могу многое сделать с примером кода, потому что это мое мнение основывалось на моем собственном опыте. Но я сделал еще несколько исследований по части ссылок, и вот что я придумал.

Я не единственный, кто думает, что ссылки на Службы, Хранилища и Заводы от Существ - это не очень хорошая идея. Я нашел похожие вопросы здесь в SO:

Есть также некоторые хорошие статьи по этой теме, особенно эта Как не вводить услуги в сущности, которая также представляет собой решение, если Вам отчаянно нужно позвонить в службу из вашей организации, названной Double Dispatch. Вот пример из статьи, перенесенной в PHP:

interface MailService
{
    public function send($sender, $recipient, $subject, $body);
}

class Message
{
    //...
    public function sendThrough(MailService $mailService)
    {
        $subject = $this->isReply ? 'Re: ' . $this->title : $this->title;
        $mailService->send(
            $this->sender, 
            $this->recipient, 
            $subject, 
            $this->getMessageBody($this->content)
        );
    }
}

Итак, поскольку вы можете видеть, что у вас нет ссылки на MailService -сервис внутри вашего Message объекта, вместо этого он передается как аргумент методу сущности. Такое же решение предлагается автором этой статьи " DDD: Сервисы" на http://devlicio.us/ в разделе комментариев.

Я также попытался взглянуть на то, что Эрик Эванс говорит об этом в своей книге "Управление доменами: постановка сложностей в сердце программного обеспечения". Кратковременно просмотрев, я не нашел точного ответа, но я нашел пример, когда Entity фактически вызывает службу статически, то есть без ссылки на нее.

public class BrokerageAccount {
    String accountNumber;
    String customerSocialSecurityNumber;

    // Omit constructors, etc.

    public Customer getCustomer() {
        String sqlQuery =
            "SELECT * FROM CUSTOMER WHERE" +
            "SS_NUMBER = '" + customerSocialSecurityNumber + "'";
        return QueryService.findSingleCustomerFor(sqlQuery);
    }

    public Set getInvestments() {
        String sqlQuery =
            "SELECT * FROM INVESTMENT WHERE" +
            "BROKERAGE_ACCOUNT = '" + accountNumber + "'";
        return QueryService.findInvestmentsFor(sqlQuery);
    }
}

В приведенной ниже записи указано следующее:

Примечание. QueryService, утилита для извлечения строк из базы данных и создание объектов, просто для объяснения примеров, но необязательно хороший дизайн для реального проекта.

Если вы посмотрите на исходный код проекта DDDSample, о котором я упомянул выше, вы увидите, что сущности не имеют ссылки на что-либо, кроме объектов в пакете model, т.е. сущности и Объекты значения. Кстати, проект DDDSample подробно описан в книге "Задача, связанная с управлением доменами: сложность в сердце программного обеспечения"...

Кроме того, еще одна вещь, которую я хотел бы поделиться с вами, - это аналогичная дискуссия о domaindrivendesign Yahoo Group. Этот message из обсуждения цитирует Эрика Эванса по предметным объектам, ссылающимся на репозитории.

Заключение

Подводя итог, ссылка на Службы, Хранилища и Заводы от Сущностей НЕ хороша. Это наиболее приемлемое мнение. Несмотря на то, что хранилища и фабрики являются гражданами уровня домена, они не являются частью проблемной области. Иногда (например, в статье Википедии о DDD) концепция доменных служб называется "Чистая фабрикация" , которая подразумевает, что класс (Service) "не представляет собой понятие в проблемной области". Я бы предпочел ссылаться на Фабрики и Хранилища как на Чистые Изготовления, потому что Эрик Эванс действительно говорит что-то еще в своей книге о концепции Услуг:

Но когда операция на самом деле является важной концепцией домена, СЕРВИС является естественной частью МОДЕЛЬ-ДВИЖЕНИЯ. Объявлено в модель как СЕРВИС, а не как фальшивый объект, который не фактически представляют что-либо, автономная операция не вводит в заблуждение кто-нибудь.

В соответствии с вышеизложенным, иногда вызов Службы от вашего лица может быть разумным. Затем вы можете использовать подход Double Dispatch, чтобы вам не пришлось ссылаться на класс "Сервис в вашем сущности".

Конечно, всегда есть люди, которые не согласны с мейнстримом, как автор статьи Доступ к доменным службам из сущностей.

Ответ 2

Если сущность должна знать о фабриках, хранилищах, службах в домен?

Сущность никогда не должна ссылаться на репозиторий или службу приложений. Допустимым для объектов ссылаться на factory, если он использует его для создания составного объекта. Также приемлемо, чтобы объект имел зависимость от службы домена, если он использует эти службы для определенного поведения.

Должен ли репозиторий знать о службах в домене?

Как правило, нет. Репозиторий должен отвечать только за сохранение.

Теперь, если я хочу добавить отношение с новым тегом. Что будет лучший способ сделать это?

Это зависит от того, на каком слое вы ссылаетесь. В типичной DDD-архитектуре вы должны иметь оба фрагмента кода. У вас будет служба приложений статей, которая инкапсулирует домен и предоставляет такой гранулированный метод, как addTag, где вы передадите идентификатор статьи и идентификатор тега. Этот метод будет извлекать соответствующие экземпляры статьи и тега (если необходимо), а затем:

$article->tags->add(TagEntity);
$articleRepository->save($article);

Все внешние слои в зависимости от этого домена будут связываться с ним через службу приложений.

Ответ 3

Я отвечу на это с предварительным условием, что я не думаю, что есть правильный ответ, просто разные подходы.

Думая о доменных объектах, я бы сказал, что первым подходом является DDD. Вы имеете дело только с объектами домена.

Я действительно думаю, что есть использование для объекта службы, который предоставляет это как часть уровня API/Service. В этом случае вы можете обернуть свой код из # 1 в свою службу №2. Это позволяет вам не подвергать объекты домена внешним пользователям - и вы можете поддерживать внешний интерфейс /API неизменным при обновлении модели домена.

Это, однако, только одно мнение.

Ответ 4

Если сущность должна знать о фабриках, хранилищах, службах в домен?

  • Приложения: нет
  • Доменные службы: да, поскольку они находятся в домене
  • Заводы: да, поскольку они находятся на уровне домена
  • Интерфейсы репозитория: да, поскольку они находятся в домене
  • Репозитории: нет, поскольку они находятся в уровне инфраструктуры

Обратите внимание на разницу между интерфейсом и реализацией: почему вы должны использовать интерфейс и реализации.

И FYI, Фабрики и Хранилища - это службы в конце концов, поэтому вы можете обобщить:

  • Сервисные интерфейсы: да, если они находятся на уровне домена

Легко

Служба домена - это та, которая определена в пределах уровня домена, хотя реализация может быть частью уровня инфраструктуры. репозиторий - это служба домена, реализация которой действительно находится в инфраструктурный уровень, а factory - также служба домена, реализация обычно находится в пределах уровня домена.

(Источник: http://www.methodsandtools.com/archive/archive.php?id=97p2)

Должен ли репозиторий знать о службах в домене?

Обычно нет, зачем это вообще? Репозиторий управляет постоянством. Но я не думаю, что это "запрещено", потому что уровень инфраструктуры (постоянство) знает о домене.

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

Предпочитайте подход ООП, когда это возможно:

$article = new Article();
$article->addTag($tag);
$articleRepository->save($article);

У меня есть смысл.

служба домена - это любая бизнес-логика, которая не легко живет внутри объекта.

(http://www.methodsandtools.com/archive/archive.php?id=97p2)

а также:

Когда значительный процесс или преобразование в домене не является естественной ответственностью объекта ENTITY или VALUE OBJECT, добавьте операцию в модель как отдельный интерфейс, объявленный как SERVICE.

(Эрик Эванс)

Подводя итог, создайте службу домена, если вы считаете нужным, это не автоматическое.

Ответ 5

Прежде всего, не подвешивайте на нем - его легко переусердствовать, создавая ненужные классы обслуживания и т.д. Меньший код хорош. Посмотрите исходный исходный материал, а код в Java или С#.

1) Если Сущность должна знать о Фабриках, Хранилищах, Сервисах в домен?

Он может, если требуется. Например, класс my (java), аннотированный с помощью @Entity, также может быть аннотирован с помощью @Configurable и включать в него сессии/другие классы. В этом и заключается суть - инкапсулировать всю необходимую бизнес-логику и выставить простой простой api, расположенный в одном классе домена.

2) Должен ли репозиторий знать о службах в домене?

Нет. Но, напротив, скорее всего, служба будет использовать репозиторий.

Службы используются, когда используется более одного домена/объекта/корневой совокупности. Таким образом, если TAG является отдельным объектом, это будет хорошо:

$articleService->addTag($article, TagEntity); 

Однако, если тег является другим корневым агрегатом, вы можете просто сделать

$article->tags->add(TagEntity);

И сама статья делает сохранение (без каких-либо других вызовов) путем размещения внутри него репозитория/dao.