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

Дал DDD, но ему нужны некоторые из его преимуществ

Я отказываюсь от традиционного DDD, который часто является огромным timewaster и заставляет меня делать бесконечное отображение: data layer <--> domain layer <--> presentation layer.

Для даже небольшого изменения я должен изменить модели данных, модели домена, модели представления/режимы отображения, затем репозитории, классы менеджера/службы и, конечно, карты AutoMapper, а затем проверить все это! Каждый вызов требует вызова слоя, который вызывает слой, который вызывает базовый код. И я ничего не получаю взамен, кроме "вам может понадобиться это в будущем". Мех.

Мой текущий подход более прагматичен:

  • Я больше не беспокоюсь о разнице между "уровнем данных" и "доменным слоем", поскольку нет смысла - термины взаимозаменяемы. Я позволяю EF делать это, и при необходимости добавлять интерфейсы и репозитории.
  • Я объединил проекты "данные" и "домен" (в "ядро", скучное имя, я знаю), и я мог почти поклясться, что Visual Studio работает быстрее.
  • Я разрешаю объектам EF идти вверх и вниз по стеку, но, как обычно, я все равно сопоставляю их с презентационными моделями/режимами просмотра.
  • Для простых операций я вызываю репозитории непосредственно из контроллеров, для сложных операций я использую администраторы/службы домена, как обычно; Хранилища никогда не выставляют IQueryable.
  • Я определяю сущности /POCOs как частичные классы, поэтому я могу добавлять поведение домена отдельно в соответствующие частичные классы.

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

Возможные решения:
1. Живи с ним. Это уродливо, но предпочтительнее проблем, описанных выше.
2. Для каждого объекта определите интерфейс, который скрывает свойства навигации; и заставить клиентский код использовать интерфейсы. Но, как это ни парадоксально, это означает еще один слой (хотя и тонкий и управляемый).
3. Что еще?

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

EDIT: Этот вопрос касается не DDD. И обратите внимание, что многие борются с традиционным подходом DDD - Кажется, что Seemann достигает того же вывода, Рахиен говорит о "Бесполезной абстракции за саму абстракцию" , и сам Эванс сказал, что DDD действительно полезен в 5% случаев. Также см. Эту тему. Некоторые из комментариев/ответов предсказуемо о том, как я делаю DDD неправильно, или как я могу настроить мою систему, чтобы сделать это правильно. Тем не менее, я не спрашиваю о DDD или избиении его для случаев, когда он подходит, скорее я хотел бы знать, что делают другие в соответствии с мышлением, которое я описал выше. Это не так, как если бы DDD был панацеей от всех проблем дизайна, каждое десятилетие выходит новый процесс (RUP - XP, Agile, Booch, blah...). DDD - это всего лишь самый новый, самый известный и используемый. Но прагматизм должен на первом месте, так как я пытаюсь построить товарную продукцию, поставляемую вовремя, и ее легко поддерживать. Самым полезным программным аксиомом, который я узнал, безусловно, является YAGNI. Я хочу изменить свою систему на своего рода "DDD-lite" , где я получаю сильную концепцию дизайна/OOP/шаблона, но без жира.

4b9b3361

Ответ 1

Типичный подход настойчивости с DDD заключается в том, чтобы сопоставить модель домена непосредственно с соответствующими таблицами. Технически, отображения все еще существуют (и обычно объявляются в коде), но нет явной модели данных, как указано lazyberezovsky.

Проблема с свойствами навигации может быть решена несколькими способами, независимо от того, используете ли вы DDD или нет. Мне не нравится подход 1, потому что это затрудняет рассуждение о вашем коде - вы никогда не знаете, какие свойства будут установлены, а какие нет. Подход 2 гораздо лучше в теории, потому что он делает очень ясным то, что требует данный запрос, и делает вещи явными - хорошая практика в целом. Подобный, но более простой и менее хрупкий подход заключается в использовании read-models, которые являются объектами, предназначенными для выполнения требований данного запроса множества запросы. В контексте DDD они позволяют отделять поведенческие объекты от запросов, которые довольно часто бывают расходящимися. Теперь сторонники DRY могут кричать ересь и прийти к вам с факелами и вилами, но на практике часто гораздо проще поддерживать модель чтения и затем субъект пытается заставить сущности выполнять требования запроса посредством интерфейсов или сложных стратегий сопоставления. Кроме того, обязанности модели чтения и модели поведения совершенно разные, поэтому DRY неприменимо.

Это не означает, что DDD применим в вашем сценарии. Часто мудрое решение избегать полноценного DDD, особенно в сценариях, которые в основном CRUD. Вы правы, чтобы быть осторожным, хорошим примером KISS и YAGNI. DDD использует преимущества, когда ваш домен состоит из сложного поведения, а не только данных. Во всяком случае, применяется шаблон модели чтения.

UPDATE

Для реализаций, которые не используют модель чтения, взгляните на "Извлечение стратегического дизайна" , где понятие стратегии выборки позволяет спецификация именно того, что требуется от базы данных, которая смягчает проблемы с навигационными свойствами. Интерес представляет также материал, упомянутый в связанном сообщении. В целом, это пытается избежать слоя косвенности, присутствующего в других подходах. Однако, на мой взгляд, использование предлагаемой стратегии получения более сложное, чем использование модели чтения, в то время как чистый результат является одним и тем же.

Ответ 2

Некоторые мысли об этом пункте:

... репозитории никогда не выставляют IQueryable... модели всегда материализовались после того, как они покинули хранилище...

Ваш вопрос помечен как "asp.net-mvc", поэтому у вас есть веб-приложение. 90% или более всех запросов будут GET-запросами, которые должны получать некоторые данные из базы данных и показывать эти данные в веб-представлении. Как часто эти данные нужны действительно объектам, а не только мешкам свойств (выбор свойств типа сущности или, возможно, состоящий из свойств нескольких объектов)?

Скажем, ваше приложение имеет 100 просмотров. Только меньшинство из них отобразит полные объекты:

  • 50 из них представляют собой списки, отображающие выбранные данные (клиент с идентификатором и адресом, но без контакта с клиентом, номер телефона и объем продаж).
  • 20 из них содержат текстовые поля автозаполнения, чтобы выбрать ссылку (заказчик для заказа, но в списке автозаполнения отображается только имя клиента и город, а не остальная часть адреса, контактное лицо, номер телефона и объем продаж и отображаются только первые 5 ударов)
  • 1 - это вид редактирования для клиента, который показывает все, но не объем продаж
  • 1 представляет собой подробный вид для клиента с его последними пятью заказами
  • 1 - это подробный вид заказа, включая элементы заказа, включая товар каждого товара, но без имени поставщика продукта
  • 1 - это тот же вид, но специализированный для отдела закупок, который хочет видеть поставщика для каждого товара и товара со средним сроком поставки поставщика за последние три месяца.
  • 1 - это вид сервисного отдела, который показывает заказ только с элементами заказа в категории "ремонт"
  • 1 вид для отдела кадров показывает сотрудников, включая фотографию, хранящуюся в виде большого блоба.
  • 1 вид для отдела планирования персонала показывает короткую версию сотрудника без фото
  • и т.д. и т.д.

Как программист пользовательского интерфейса, у меня были бы все требования к данным для визуализации представления с приведенными выше примерами:

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

Как выполнить эти требования в качестве разработчика доступа к данным/репозитория/службы?

  • Я предоставляю лишь несколько методов и материализую объектов: заголовок заказа загрузки, заголовок заказа загрузки с элементами, заголовок заказа загрузки с элементами и продуктом, заголовок заказа загрузки с элементами и продуктом и поставщиком, загружает заголовок клиента (бросает 15 из 20 свойств, уважаемый пользовательский интерфейс, если вам нужны только пять свойств), загрузите заголовок клиента со всеми 3000 заказами (выбросьте 2995 прочь, уважаемый пользовательский интерфейс, если вам нужно только пять) и т.д. И т.д. Я возвращаю интерфейсы из репозиториев которые скрывают не загруженные свойства навигации.
  • Я забочусь обо всех деталях, которые нужны пользовательскому интерфейсу: я создаю методы репозитория/службы, такие как GetFiveCustomerPropertiesForAutoComplete, GetCustomerWithLastFiveOrders и т.д. и т.д. Я возвращаю интерфейсы из репозиториев, которые скрывают свойства (также скалярные). У меня нет загружен. Или я возвращаю "DTOs", которые содержат запрошенные свойства. Я меняю репозиторий/службы и создаю новые DTO каждый день, когда разработчик пользовательского интерфейса звонит с требованиями к данным для следующего представления.
  • Я возвращаю IQueryable<TEntity> из репозиториев и расскажу разработчику пользовательского интерфейса "самостоятельно создайте запрос LINQ для получения данных, необходимых для ваших просмотров". (На следующее утро администратор базы данных жалуется на сотни ужасных исполняемых запросов к базе данных.)
  • Я возвращаю "подготовленный" IQueryable<TEntity> из репозиториев/служб, которые охватывают, например, проблемы безопасности, такие как применение предложений Where для прав доступа пользователя или добавление предложения Where для поискового запроса или применение NoTracking для запроса. Я говорю разработчику пользовательского интерфейса: "Вам разрешено расширять запрос с помощью a) проекций (Select), b) подкачки (Take и Skip) и, возможно, c) сортировка (OrderBy), потому что я рассматриваю эти три части запроса в качестве пользовательского интерфейса. Все остальные требования к запросу (фильтрация, объединение, группировка и т.д.) должны быть реализованы на уровне репозитория/службы и запрещены на уровне пользовательского интерфейса." Наиболее важной частью здесь являются прогнозы, которые материализуют ViewModels непосредственно через запрос LINQ/SQL без промежуточного слоя сопоставления и без накладных расходов для загрузки более необходимых столбцов/свойств.

Это лишь некоторые мысли. Каждый подход имеет свои преимущества и недостатки. Работая в небольших командах, где, по крайней мере, один или несколько разработчиков имеют обзор того, что происходит как в репозитории/службе, так и в слое UI/ "project", последний вариант отлично подходит для меня в моем опыте, хотя он не всегда работает с строгие правила описываются (например, фильтр по категории товаров для включенных элементов заказа для заказа требует применения предложения Where внутри проекции, то есть в слое пользовательского интерфейса). Для запросов POST и модификаций данных я бы использовал DTO, которые отправляют данные, полученные из представления обратно, в службу, подлежащую обработке там.

Для более строгого разделения "слоя запроса" и уровня пользовательского интерфейса я бы предпочел бы что-то близкое ко второму варианту, возможно, не с интерфейсом /DTO для каждого требования к пользовательскому интерфейсу, но каким-то образом сводится к набору DTO для наиболее распространенных требований (с ценой небольшой надбавки иногда излишне загруженных свойств). Однако я ожидаю, что это будет больше работы, чем последний вариант из-за большего количества необходимых методов репозитория/обслуживания, дополнительного обслуживания (возможно, многих) DTO и промежуточного сопоставления между DTO и ViewModels.

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

Ответ 3

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

Многие люди сталкиваются с вашей проблемой. Отображение - это свободный налог на связь в стране статической типизации. Возможно, более динамичный язык может решить некоторые из ваших страданий. Или, может быть, вы можете найти добродетель в автоматизации больше (DSL, MDA). Вместо этого вы можете переключиться на клиентский сервер.

Интерфейсы не являются слоями, а скорее абстракциями. Используйте их с умом.

Лично я никогда не буду использовать эти ярлыки. Было слишком много укусов, пытаясь пропустить шаги. Логика начинает появляться в нечетных местах. Если у меня есть приложение, управляемое данными, для разработки простых наборов данных, приходите на ум, EF также. Но я не называю совокупность объектов или сущность в смысле DDD, просто сущность в смысле ERD. Transactionscript может быть лучше, чем выполнять частичный метод спринклера. Что касается читаемых объектов модели, это не слои косвенности.

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

Ответ 4

Я просто постараюсь быть коротким - мы пошли на метод 2 - то есть добавим слой интерфейсов, который вы используете на клиенте. Вы можете создать EF для них, просто немного подстроить шаблоны .tt.

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

Ответ 5

Проблема: теперь я использую сущности повсюду, поэтому код клиента могут видеть свои навигационные свойства.

Я не совсем понимаю, почему это проблема и как она связана с объектами EF в частности. По коду клиента вы имеете в виду код уровня представления или любой код, потребляющий ваши объекты?

Для кода пользовательского интерфейса простым решением является определение ViewModels, которое просто не раскрывает эти свойства навигации (или выставлять только некоторые из них в зависимости от глубины графа объекта, которые нужны вашим GUI).

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

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

  • Я больше не беспокоюсь о различии между слоем "данные" и "доменным слоем", поскольку нет смысла - термины взаимозаменяемы. Я позволяю EF делать это и добавлять интерфейсы и репозитории сверху, если это необходимо.
  • Я объединил свои проекты "данные" и "домен" (в "ядро", скучное имя, я знаю), и я мог почти поклясться, что Visual Studio - это на самом деле работает быстрее.
  • Я разрешаю объектам EF идти вверх и вниз по стеку, но я по-прежнему сопоставляю их с моделями презентаций/режимами просмотра, как обычно.
  • Для простых операций я вызываю репозитории непосредственно из контроллеров, для сложных операций я использую администраторы/службы домена как обычный; репозитории никогда не выставляют IQueryable.
  • Я определяю сущности /POCOs как частичные классы, поэтому я могу добавлять поведение домена отдельно в соответствующие частичные классы.

Ни одна из этих вещей, по-видимому, не является для меня принципиально анти-DDD, кроме разделения данных/доменов.

В частности, если вы сначала используете EF-DDD с базой данных, это явно ориентированный на домен подход, и вы не должны определять свои таблицы перед определением своих объектов. Также неясно, взаимодействуют ли некоторые из ваших доменных объектов с базой данных или EF (а не с DDD - и в целом, с многоуровневой архитектурой) или систематически имеют объекты доступа к данным между ними (совместимость с DDD).