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

Не имея проблем с реализацией реальной логики в домене DDD

Несмотря на то, что мы долгое время изучали 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() (служба приложения?)

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

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

4b9b3361

Ответ 1

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

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

void ICanSendConfirmationEmail(EmailAddress address, ...)

или

void ICanNotifyUserOfSuccessfulRegistration(EmailAddress address, ...)

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

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

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

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

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

Ответ 2

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 3

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

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