Возникли проблемы с размещением реальной логики на уровне домена DDD - PullRequest
19 голосов
/ 05 сентября 2011

Несмотря на то, что я изучал Domain Driven Design в течение долгого времени, есть некоторые основы, которые я просто выясняю.

Кажется, что каждый раз, когда я пытаюсь создать богатый domain layer, мне все еще нужно много Domain Services или толстого Application Layer, и я получаю кучу почти анемичных сущностей домена без реального логика в них, кроме «GetTotalAmount» и тому подобное. Ключевой вопрос заключается в том, что сущности не знают о внешнем материале, и это плохая практика - вводить что-либо в сущности.

Позвольте привести несколько примеров:

1. Пользователь подписывается на услугу. Пользователь сохраняется в базе данных, файл создается и сохраняется (необходим для учетной записи пользователя) и отправляется электронное письмо с подтверждением.

Пример с подтверждением по электронной почте широко обсуждался в других темах, но без реального заключения. Некоторые предлагают поместить логику в application service, который получает EmailService и FileService, введенные из infrastructure layer. Но тогда у меня была бы бизнес-логика вне домена, верно? Другие предлагают создать domain service, который вводит infrastructure services, но в этом случае мне нужно было бы иметь интерфейсы infrastructure services внутри domain layer (IEmailService и IFileService), которые не выглядят слишком хорошо (потому что domain layer не может ссылаться на infrastructure layer). А другие предлагают реализовать События домена Udi Dahan и затем подписать EmailService и FileService на эти события. Но это похоже на очень слабую реализацию - и что произойдет, если службы потерпят неудачу? Пожалуйста, дайте мне знать, что вы считаете правильным решением здесь.

2. Песня приобретена в магазине цифровой музыки. Корзина пуста. Покупка сохраняется. Платежная служба называется. Подтверждение по электронной почте отправлено.

Хорошо, это может быть связано с первым примером. Вопрос здесь в том, кто отвечает за организацию этой транзакции? Конечно, я мог поместить все в контроллер MVC с внедренными сервисами. Но если я хочу настоящий DDD, вся бизнес-логика должна быть в домене. Но какой субъект должен иметь метод «Покупка»? Song.Purchase()? Order.Purchase()? OrderProcessor.Purchase() (услуга домена)? ShoppingCartService.Purchase() (служба приложений?)

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

Надеюсь, эти примеры достаточно ясны, чтобы показать проблемы, с которыми я имею дело.

Ответы [ 3 ]

11 голосов
/ 06 сентября 2011

Ответ Дмитрия указывает на некоторые хорошие вещи, которые нужно искать.Часто / легко вы попадаете в свой сценарий, когда данные перемещаются от БД к GUI через разные уровни.

Меня вдохновил простой совет Джимми Нильссона "Объекты-ценности, объекты-ценности и другие объекты-ценности".Часто люди, как правило, сосредотачиваются на существительных и моделируют их как сущность.Естественно, у вас часто возникают проблемы с поиском поведения DDD.Глаголы легче ассоциировать с поведением.Хорошо, чтобы эти глаголы появлялись в вашем домене как объекты Value.

Некоторые рекомендации, которые я использую для себя при разработке домена (должны сказать, что для создания богатого домена требуется время, часто несколькоитерации рефакторинга ...):

  • Свернуть свойства (получить / установить)
  • Использовать как можно больше объектов-значений
  • Предоставлять как можно меньше.Сделайте ваши доменные агрегаты интуитивно понятными.

Не забывайте, что ваш домен может быть богатым, выполняя валидацию.Только ваш домен знает, как совершить покупку, и что требуется.

Ваш домен также должен отвечать за проверку, когда ваши сущности переходят из одного состояния в другое состояние (проверки рабочего процесса).

Я приведу несколько примеров: Вот статья, которую я написал в своем блоге, касающаяся вашей проблемы с анемичным доменом. http://magnusbackeus.wordpress.com/2011/05/31/preventing-anemic-domain-model-where-is-my-model-behaviour/

Я также могу порекомендовать статью в блоге Джимми Богарда о проверке сущностей и использованииШаблон валидатора вместе с методами расширения.Это дает вам свободу проверять инфраструктурные объекты, не загрязняя ваш домен: http://lostechies.com/jimmybogard/2007/10/24/entity-validation-with-visitors-and-extension-methods/

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

В вашем первом примере (просто мозговой штурм сейчас, чтобы заставить наш разум больше думать о ценностных объектах).

  1. Ваш MusicService.AddSubscriber(User newUser) Служба приложений получает звонок от ведущего / контроллера / WCF с новым пользователем.Сервис уже получил IUserRepository и IMusicServiceRepository, введенный в ctor.
  2. Музыкальный сервис "Spotify" загружается через IMusicServiceRepository
  3. Сущность musicService.SignUp(MusicServiceSubscriber newSubsriber) Метод принимает объект Value MusicServiceSubscriber.Этот объект Value должен принимать User и другие обязательные объекты в ctor (объекты значения являются неизменяемыми).Здесь вы также можете разместить логику / поведение, например дескриптор subscriptionId и т. Д.
  4. Что и делает метод SignUp, он запускает событие домена NewSubscriberAddedToMusicService.Это поймано EventHandler HandleNewSubscriberAddedToMusicServiceEvent, которому IFileService и IEmailService ввели в его ctor.Реализация этого обработчика находится на уровне службы приложений, НО событие контролируется доменом и MusicService.SignUp.Это означает, что домен находится под контролем.Eventhandler создает файл и отправляет электронное письмо.

Вы можете сохранить пользователя с помощью обработчика событий ИЛИ сделать метод MusicService.AddSubscriber(...) для этого.Оба сделают это через IUserRepository, но это вопрос вкуса и, возможно, как это отразит реальную область.

Наконец ... Я надеюсь, что вы поняли что-то из вышеперечисленного ... во всяком случае.Самое важное - начать добавлять методы «Глаголы» к сущностям и делать совместную работу.В вашем домене также могут быть объекты, которые не сохраняются, они существуют только для передачи между несколькими объектами домена и могут содержать алгоритмы и т. Д.

8 голосов
/ 05 сентября 2011

Пользователь подписывается на услугу. Пользователь сохраняется в база данных, файл генерируется и сохраняется (необходим для учетной записи пользователя), и подтверждение по электронной почте отправлено.

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

void ICanSendConfirmationEmail(EmailAddress address, ...)

или

void ICanNotifyUserOfSuccessfulRegistration(EmailAddress address, ...)

Интерфейс может использоваться другими классами домена. Реализуйте этот интерфейс на уровне инфраструктуры, используя реальные классы SMTP. Внедрить эту реализацию при запуске приложения. Таким образом, вы указали бизнес-намерение в коде домена, а логика вашего домена не имеет прямой ссылки на инфраструктуру SMTP. Ключевым здесь является имя интерфейса, оно должно быть основано на вездесущем языке.

Песня приобретена в магазине цифровой музыки. Корзина покупок опустошен. Покупка сохраняется. Платежная служба называется. Подтверждение по электронной почте отправлено. Хорошо, это может быть связано с первым примером. Вопрос в том, кто отвечает за организацию этой транзакции?

Использование передового опыта ООП для распределения обязанностей (GRASP и SOLID). Модульное тестирование и рефакторинг обеспечат вам обратную связь. Сама оркестровка может быть частью thin Application Layer. От Многоуровневая архитектура DDD :

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

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

0 голосов
/ 05 сентября 2011

Большая часть ваших запросов связана с объектно-ориентированным проектированием и распределением ответственности, вы можете подумать о GRASP Patterns и Это , вы можете воспользоваться книгами по объектно-ориентированному дизайну, рекомендуем следующий

Применение UML и шаблонов

...