Ответ 1
Объекты и объекты ценности никогда не должны зависеть ни от чего, кроме друг от друга. Это самые фундаментальные из всех строительных блоков DDD. Они представляют собой концепции вашей проблемной области и поэтому должны сосредоточиться на проблеме. Заставляя их зависеть от фабрик, хранилищ и служб, вы делаете это размытие фокуса. Существует еще одна проблема с ссылкой на Службы в объектах и объектах Value. Поскольку службы также обладают логикой домена, у вас возникнет соблазн делегировать некоторые из обязанностей модели домена на службы, которые в конечном итоге могут привести к Модель анемичного домена.
Заводы и Хранилища - это просто помощники, используемые для создания и сохранения сущностей. В большинстве случаев они просто не имеют аналогии в реальном проблемном домене, поэтому наличие ссылок на "Фабрики и хранилища" на "Службы" и "Объекты" не имеет смысла в соответствии с логикой домена.
Что касается примера, который вы указали, так я его реализую
$article->addTag($tag);
$articleRepository->save($article);
Я бы не дал прямой доступ к базовой коллекции, потому что я мог бы хотеть, чтобы Article
выполнял некоторую логику домена (наложение ограничений, проверку) на Tag
, прежде чем добавлять ее в коллекцию.
Избегайте этого
$articleService->addTag($article, $tag);
Использовать службы только для выполнения операций, которые не принадлежат ни одному объекту концептуально. Сначала попробуйте подогнать его к Entity, убедитесь, что он не подходит. И только тогда используйте Сервис для него. Таким образом, вы не получите анонимных моделей домена.
ОБНОВЛЕНИЕ 1
Цитата из книги Эрика Эванса "Проект, управляемый доменами: проблема сложности в сердце программного обеспечения":
УСЛУГИ следует использовать разумно и не разрешать ОБЪЕКТЫ И ЦЕННЫЕ ОБЪЕКТЫ всех их действий.
ОБНОВЛЕНИЕ 2
Кто-то отказался от этого ответа, и я не знаю, почему. Я могу только подозревать причину. Это могут быть ссылки между Entities and Services или это может быть пример кода. Ну, я не могу многое сделать с примером кода, потому что это мое мнение основывалось на моем собственном опыте. Но я сделал еще несколько исследований по части ссылок, и вот что я придумал.
Я не единственный, кто думает, что ссылки на Службы, Хранилища и Заводы от Существ - это не очень хорошая идея. Я нашел похожие вопросы здесь в SO:
- Доступно ли объектам доступ к репозиториям?
- DDD - Зависимости между моделью домена, сервисами и репозиториями
- DDD - правило, что объекты не могут напрямую обращаться к репозиториям
- Объекты DDD, использующие Услуги.
Есть также некоторые хорошие статьи по этой теме, особенно эта Как не вводить услуги в сущности, которая также представляет собой решение, если Вам отчаянно нужно позвонить в службу из вашей организации, названной 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, чтобы вам не пришлось ссылаться на класс "Сервис в вашем сущности".
Конечно, всегда есть люди, которые не согласны с мейнстримом, как автор статьи Доступ к доменным службам из сущностей.