Где мы проводим грань между делегированием и инкапсуляцией бизнес-логики? Мне кажется, что чем больше мы делегируем, тем более анемичным мы становимся. Тем не менее, делегирование также способствует повторному использованию и принципу DRY. Так что уместно делегировать и что должно остаться в наших моделях доменов?
Возьмите следующие проблемы в качестве примеров:
Авторизация . Должен ли объект домена отвечать за поддержание своих правил контроля доступа (таких как свойство CanEdit) или он должен быть делегирован другому компоненту / услуге, единолично отвечающему за управление доступом, например, IAuthorizationService.CanEdit (object)? Или это должно быть сочетание двух? Возможно, у объекта домена есть свойство CanEdit, которое делегирует внутреннему IAuthorizationService для выполнения фактической работы?
Проверка . То же обсуждение, что и выше, относится к валидации. Кто поддерживает правила и кто отвечает за их оценку? С одной стороны, состояние объекта должно принадлежать этому объекту, а валидность - это состояние, но мы не хотим переписывать код, используемый для оценки правил для каждого объекта домена. Мы могли бы использовать наследование в этом случае ...
Создание объекта . Фабричный класс в сравнении с фабричными методами против «обновления» экземпляра. Если мы используем отдельный класс фабрики, мы можем изолировать и инкапсулировать логику создания, но за счет открытия состояния нашего объекта для фабрики. Этим можно управлять, если наш уровень домена находится в отдельной сборке, предоставляя внутренний конструктор, используемый фабрикой, но это становится проблемой, если существует несколько шаблонов создания. И если все, что делает фабрика, - это вызывать правильного конструктора, какой смысл иметь фабрику?
Методы фабрики в классе устраняют проблему с открытием внутреннего состояния объекта, но поскольку они статичны, мы не можем нарушать зависимости посредством внедрения фабричного интерфейса, как мы можем с отдельным фабричным классом.
Постоянство . Можно утверждать, что, если наш объект домена собирается предоставлять CanEdit, делегируя ответственность за проверку прав доступа другой стороне (IAuthorizationService), почему бы не использовать метод Save для нашего объекта домена, который делает то же самое? Это позволило бы нам оценить внутреннее состояние объекта, чтобы определить, можно ли выполнить операцию, не нарушая инкапсуляцию. Конечно, это требует, чтобы мы внедрили экземпляр репозитория в наш объект домена, что немного пахнет для меня, так что мы вместо этого вызываем событие домена и позволяем обработчику выполнять операцию персистентности?
Видишь, куда я иду с этим?
У Рокфорда Лотки есть отличная дискуссия о причинах, по которым он выбрал маршрут Class-in-Charge для своей платформы CSLA, и у меня есть немного истории с этой платформой, и я могу увидеть его идею о том, что бизнес-объекты во многом параллельны объектам домена. Но, пытаясь стать более приверженными хорошим идеалам DDD, мне интересно, когда сотрудничество становится слишком много.
Если я получу сервис IAuthorizationService, IValidator, IFactory и IRepository для моего совокупного корня, что останется? Достаточно ли наличия метода Publish, который изменяет состояние объекта с «Черновик» на «Опубликованный», чтобы считать класс неанемичным доменным объектом?
Твои мысли?
источник
Ответы:
Большая часть путаницы, кажется, связана с функциональностью, которой вообще не должно быть в модели предметной области:
Постоянство никогда не должно быть в доменной модели. Никогда Вот почему вы полагаетесь на абстрактные типы, например,
IRepository
если часть модели когда-либо должна что-то делать, например, извлекать другую часть модели и использовать внедрение зависимостей или какой-либо подобный метод, чтобы связать реализацию. Так что вычеркните это из записи.Авторизация, как правило, не является частью модели вашего домена, если только она на самом деле не является частью домена, например, если вы пишете программное обеспечение безопасности. Механизм того, кому разрешено выполнять то, что в приложении, обычно обрабатывается на «краю» бизнес-уровня / домена, общедоступных частей, с которыми на самом деле разрешено общаться частям пользовательского интерфейса и интеграции - Контроллер в MVC, Сервисы или сама система обмена сообщениями в SOA ... вы понимаете.
Фабрики (и я предполагаю, что вы имеете в виду абстрактные фабрики здесь) не так уж плохи в модели предметной области, но они почти всегда не нужны. Обычно у вас есть фабрика, когда внутренняя механика создания объекта может измениться. Но у вас есть только одна реализация модели предметной области, что означает, что когда-либо будет только один вид фабрики, которая всегда вызывает одни и те же конструкторы и другой код инициализации.
Вы можете иметь «удобные» фабрики, если хотите - классы, которые инкапсулируют общие комбинации параметров конструктора и т. Д. - но, честно говоря, вообще говоря, если у вас много фабрик, сидящих в вашей доменной модели, тогда вы просто теряете строки кода.
Так что, как только вы перебираете все это, остается только проверка. Это единственный хитрый способ.
Валидация является частью вашей доменной модели, но она также является частью любого другого компонента приложения. Ваш пользовательский интерфейс и база данных будут иметь свои собственные, похожие, но разные правила проверки, основанные на схожей, но другой концептуальной модели. На самом деле не определено, нужно ли объектам иметь
Validate
метод, но даже если они это сделают, они обычно делегируют его классу валидатора (не интерфейс - валидация не является абстрактной в модели предметной области, она фундаментальна).Имейте в виду, что валидатор все еще технически является частью модели; его не нужно присоединять к объединенному корню, потому что он не содержит никаких данных или состояния. Доменные модели - это концептуальные вещи, обычно физически переводимые в сборку или коллекцию сборок. Не подчеркивайте «анемичную» проблему, если ваш код делегирования находится в непосредственной близости от объектной модели; это все еще имеет значение.
Что все это на самом деле сводится к тому, что если вы собираетесь сделать DDD, вы должны понимать , что домен является . Если вы все еще говорите о таких вещах, как постоянство и авторизация, то вы на неправильном пути. Домен представляет рабочее состояние системы - физические и концептуальные объекты и атрибуты. Все, что не имеет прямого отношения к самим объектам и отношениям, не принадлежит модели предметной области, точка.
Как правило, при рассмотрении вопроса о том, относится ли что-либо к модели предметной области, задайте себе следующий вопрос:
"Может ли эта функциональность когда-либо измениться по чисто техническим причинам?" Другими словами, не из-за каких-либо заметных изменений в реальном бизнесе или области?
Если ответ «да», то он не относится к модели предметной области. Это не часть домена.
Есть очень хороший шанс, что когда-нибудь вы измените свою инфраструктуру постоянства и авторизации. Следовательно, они не являются частью домена, они являются частью приложения. Это также относится к алгоритмам, таким как сортировка и поиск; вам не следует вставлять реализацию бинарного поискового кода в модель вашего домена, потому что ваш домен имеет дело только с абстрактной концепцией поиска, а не с тем, как он работает.
Если после того, как вы удалили все вещи, которые не имеют значения, вы обнаружите, что модель предметной области действительно анемична , то это должно служить довольно хорошим показателем того, что DDD - просто неправильная парадигма для вашего проекта.
Некоторые домены действительно являются анемией. Приложения для социальных закладок на самом деле не имеют особого «домена», о котором можно говорить; все ваши объекты в основном просто данные без функциональности. Система продаж и CRM, с другой стороны, имеет довольно сложную область; когда вы загружаете
Rate
сущность, то вполне разумно ожидать, что вы действительно сможете делать что-то с этой ставкой, например, применять ее к количеству заказа и просить, чтобы он вычислял скидки по объему, промо-коды и все эти забавные вещи.Объекты домена , которые просто содержат данные , как правило , действительно означают , что у вас есть анемия модели предметной области, но это не обязательно означает , что вы создали плохой дизайн - это может просто означать , что сам домен анемия , и что вы должны использовать другая методология.
источник
Нет. Авторизация - это забота сама по себе. Команды, которые не были бы действительными из-за отсутствия разрешений, должны быть отклонены перед доменом, как можно раньше - что означает, что часто мы даже хотим проверить авторизацию потенциальной команды, чтобы построить пользовательский интерфейс (чтобы не даже показать пользователю возможность редактирования).
Совместное использование стратегий авторизации между уровнями (в пользовательском интерфейсе и далее в сервисе или в обработчике команд) легче, когда авторизация разделяется отдельно от модели предметной области.
Одна сложная часть, с которой можно столкнуться, - это контекстная авторизация, где команда может или не может быть разрешена не только на основе ролей пользователя, но и бизнес-данных / правил.
Я бы тоже сказал нет, не в домене (в основном). Валидация происходит в разных контекстах, и правила валидации часто различаются в разных контекстах. При рассмотрении данных, инкапсулированных в агрегат, редко встречается простое, абсолютное чувство достоверности или недопустимости.
Также, как и при авторизации, мы используем логику проверки на разных уровнях - в пользовательском интерфейсе, в обработчике службы или команды и т. Д. Опять же, проще использовать DRY с проверкой, если это отдельный компонент. С практической точки зрения валидация (особенно при использовании каркасов) требует раскрытия данных, которые в ином случае должны быть инкапсулированы, и часто требует привязки пользовательских полей к полям и свойствам. Я предпочитаю, чтобы они были на других классах, чем мои доменные модели.
Я бы предпочел продублировать некоторые свойства в паре похожих классов, чем пытаться навязать требования для структуры проверки в мои сущности. Это неизбежно приводит к путанице классов сущностей.
Я использую один слой косвенности. В своих последних проектах, это команда + обработчик для создания чего - то, то есть
CreateNewAccountCommand
. Альтернативой может быть всегда использовать фабрику (хотя это может быть неудобно, если остальная часть операции сущностей предоставляется классом обслуживания, который отделен от класса фабрики).В целом, однако, я стараюсь быть более гибким с выбором дизайна для создания объекта.
new
это легко и знакомо, но не всегда достаточно. Я думаю, что здесь важно использовать суждение и позволить различным частям системы использовать разные стратегии по мере необходимости.Это редко хорошая идея; Я думаю, что есть много общего опыта, чтобы поддержать это.
Возможно, доменная модель не является правильным выбором для этой части вашего приложения.
источник
Хорошо, здесь идет для меня. Я предвосхищу это, сказав, что:
Преждевременная оптимизация (и это включает в себя дизайн) часто может вызвать проблемы.
IANMF (я не Мартин Фаулер);)
Грязный маленький секрет в том, что для небольших проектов (даже, возможно, средних) важна последовательность вашего подхода.
авторизация
Для меня аутентификация и авторизация - это сквозная проблема. В моем счастливом маленьком мире Java это делегируется безопасности Spring или фреймворку Apache Shiro.
Валидация Для меня валидация является частью объекта, так как я вижу, что он определяет, что это за объект.
например, объект Car имеет 4 колеса (хорошо, есть некоторые странные исключения, но давайте пока проигнорируем странный 3-колесный автомобиль). Автомобиль просто недействителен, если у него нет 4 (в моем мире), так что проверка является частью того, что является определением автомобиля. Это не значит, что у вас не может быть вспомогательных классов проверки.
В моем счастливом мире Java я использую платформы проверки Bean и использую простые аннотации в большинстве моих полей Bean. Тогда легко проверить ваш объект независимо от того, в каком слое вы находитесь.
Создание объекта
Я смотрю Фабричные классы с осторожностью. Слишком часто я вижу
xyxFactoryFactory
класс;)Я склонен просто создавать
new
объект по мере необходимости, пока не столкнусь со случаем, когда внедрение зависимостей оправдано (и поскольку я пытаюсь следовать подходу TDD, это встречается чаще, чем нет).В моем счастливом мире Java все больше и больше Guice, но Spring по-прежнему здесь король.
Упорство
Так что это дебаты, которые идут в кругах и каруселях, и я всегда думаю об этом.
Некоторые говорят, что если вы смотрите на объект «чисто», постоянство не является основным свойством, это просто внешняя проблема.
Другие придерживаются мнения, что ваши доменные объекты неявно реализуют интерфейс «сохраняемости» (да, я знаю, что я растягиваюсь здесь). Поэтому хорошо иметь различные
save
иdelete
т. Д. Методы на них. Это рассматривается как прагматичный подход, и многие технологии ORM (JPA в моем счастливом мире Java) работают с объектами таким образом.Исходя из сквозной проблемы безопасности, я проверяю, правильно ли установлены права на редактирование / удаление / добавление / любые другие разрешения для службы, которая вызывает метод сохранения / обновления / удаления объекта. Если я действительно параноик, я мог бы даже установить разрешения для самого объекта домена.
НТН!
источник
Джимми Нильссон затрагивает эту тему в своей книге о DDD. Он начал с анемичной модели, перешел к неанемичным моделям в более позднем проекте и, наконец, остановился на анемичных моделях. Он рассуждал о том, что анемичные модели могут быть повторно использованы в нескольких сервисах с различной бизнес-логикой.
Компромисс - отсутствие способности открывать. Методы, которые вы можете использовать для работы с анемичными моделями, распространены по всему набору сервисов, расположенных в других местах.
источник
Этот вопрос задавался давным-давно, но он помечен как Domain Driven Design. Я думаю, что сам вопрос содержит фундаментальное недопонимание всей практики, и ответы, включая принятый ответ, увековечивают фундаментальное недопонимание.
В архитектуре DDD нет «модели предметной области».
Давайте возьмем авторизацию в качестве примера. Позвольте мне попросить вас подумать над вопросом: представьте, как в вашей системе проходят аутентификацию два разных пользователя. Один пользователь имеет право изменить определенную сущность, а другой - нет. Почему нет?
Я ненавижу простые и надуманные примеры, потому что они часто путают больше, чем просвещают. Но давайте представим, что у нас есть два разных домена. Во-первых, это платформа CMS для маркетингового агентства. У этого агентства есть много клиентов, у которых есть контент в Интернете, которым должны управлять копирайтеры и художники-графики. Содержание включает в себя сообщения в блоге, а также целевые страницы для разных клиентов.
Другой домен - управление запасами для обувной компании. Система управляет товарно-материальными запасами, начиная с момента их доставки от производителя во Франции, до распределительных центров в континентальной части США, в розничные магазины на местных рынках и, наконец, до покупателя, который покупает обувь в розничной торговле.
Если вы считаете, что правила авторизации одинаковы для обеих компаний, то да, это было бы хорошим кандидатом на услугу за пределами домена. Но я сомневаюсь, что правила авторизации одинаковы. Даже концепции, стоящие за пользователями, будут другими. Конечно, язык будет другим. У маркетингового агентства, вероятно, есть роли, такие как автор поста и владелец актива, в то время как у обувной компании, вероятно, есть такие роли, как клерк доставки, менеджер склада или менеджер магазина.
С этими понятиями, вероятно, связаны всевозможные правила разрешений, которые необходимо смоделировать в домене. Но это не значит, что они являются частью одной модели даже в одном приложении. Потому что помните, что существуют разные ограниченные контексты.
Поэтому, возможно, можно рассматривать неанемичную модель домена в контексте авторизации как отличную от контекста маршрутизации поставок обуви в магазины с низким запасом или перенаправления посетителей сайта на соответствующую целевую страницу в зависимости от того, какое объявление они нажимали.
Если вы столкнулись с анемичной моделью предметной области, возможно, вам просто нужно потратить больше времени на сопоставление контекста, прежде чем начинать писать код.
источник