Почему я должен изолировать объекты моей предметной области от уровня представления?

85

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

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

Итак, я ищу конкретные причины, которые я могу использовать, чтобы подтвердить это. В частности:

  1. Почему мы не должны использовать объекты предметной области на нашем уровне представления?
    (если ответ очевиден - «развязка», объясните, почему это важно в данном контексте)
  2. Следует ли нам использовать дополнительные объекты или конструкции, чтобы изолировать объекты нашей предметной области от интерфейса?
Марк Роджерс
источник
этот вопрос должен быть в вики.
Сайед Тайяб Али
@ m4bwav - это должна быть вики, потому что она сформулирована таким образом, чтобы вызвать обсуждение, а не единственный правильный ответ.
Роб Аллен,
1
@ m4bwav: Я думаю, ваш вопрос был скорее выражением мнения, чем реальным вопросом ... Я пытался исправить это (вы можете захотеть отредактировать его дальше), но имейте в виду, что без должного внимания это может показаться троллинг.
Shog9
5
Хорошо, бэкап, я задаю законный вопрос, как это кого-нибудь обидит? На кого я ориентируюсь?
Марк Роджерс
@ m4bwav: вы нацелены на своего соломенного человечка. «Большое количество людей», с которыми вы обсуждаете это в своем вопросе.
Shog9

Ответы:

48

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

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

Поль Сонье
источник
2
Что вы имеете в виду, говоря «реализовать объекты домена в двух местах»?
jlembke 02
10
Мне это кажется глупым. Почему нужно делать дополнительную работу сейчас, которая МОЖЕТ сохранить работу в будущем? В 9 случаях из 10 вам никогда не понадобится вносить изменения, которые сэкономили бы «ТОННЫ» работы.
Beep beep
13
@LuckyLindy: в 99 случаях из 100 (на самом деле больше) пристегивать ремень безопасности не обязательно, чтобы я не получил травму. Однако в том случае, когда мне это действительно нужно, это (вероятно) убережет меня от смерти или серьезных травм. Унция профилактики стоит фунта лечения. Я подозреваю, что ваше мнение об этом изменится после того, как вы наберетесь опыта.
Paul Sonier
19

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

Что ж, вместо загрузки объекта CompanyObject, который может иметь ссылки на подписки или кто знает что еще, я мог бы отправить обратно DTO с именем и идентификатором. ИМХО, это хорошее применение.

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

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

Идея, что кто-то поместит валидацию в свой бизнес-объект? Что ж, я говорю, что это хорошо. Ваш пользовательский интерфейс не должен нести исключительную ответственность за проверку ваших бизнес-объектов. Ваш бизнес-уровень ДОЛЖЕН проводить собственную проверку.

Зачем помещать код генерации пользовательского интерфейса в бизнес-объект? В моем случае у меня есть отдельные объекты, которые генерируют код пользовательского интерфейса отдельно от пользовательского интерфейса. У меня есть объекты sperate, которые отображают мои бизнес-объекты в Xml, идея о том, что вы должны разделять свои слои, чтобы предотвратить этот тип загрязнения, мне настолько чужда, потому что зачем вам вообще помещать код генерации HTML в бизнес-объект ...

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

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

Джош Берке
источник
Разве метки, различающиеся для разных арендаторов, не означают, что каждый арендатор использует разные языки? Я думаю, что должна существовать концепция метамодели, в которой домен разделяется между арендаторами с уровнем перевода для их интерпретации метамодели.
Kell
16

Вы делаете это по той же причине, по которой храните SQL на своих страницах ASP / JSP.

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

Вы хотите повторно использовать этот изящный виджет пользовательского интерфейса в другом приложении? Итак, вам нужно создать базу данных с этим именем, этими двумя схемами и этими 18 таблицами. Вы также должны настроить Hibernate и Spring (или выбранные вами фреймворки) для проверки бизнеса. О, вы также должны включить эти 85 других не связанных классов, потому что на них есть ссылки на бизнес-уровне, который просто находится в том же файле.

digitaljoel
источник
13

Я не согласен.

Я думаю, что лучше всего начать с объектов предметной области на уровне представления, пока не появится смысл поступать иначе.

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

Даниэль Алексюк
источник
Спасибо за ваш вклад, я понимаю, откуда вы. Хотя я не говорю, что это не еще один из бесконечных способов создания успешного проекта, он, похоже, противоречит стилю «Domain-Driven Design», который предназначен для более крупных и сложных проектов, которые сложнее поддерживать. в долгосрочной перспективе.
Марк Роджерс
Нет, это неправильно, и именно поэтому так много сайтов становятся уязвимыми для внедрения sql.
Реми
7

Ответ зависит от масштаба вашего приложения.


Приложение Simple CRUD (создание, чтение, обновление, удаление)

Для базовых приложений crud у вас нет никакой функциональности. Добавление DTO поверх сущностей будет пустой тратой времени. Это увеличило бы сложность без увеличения масштабируемости.

введите описание изображения здесь


Умеренно сложное приложение без CRUD

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

Добавление DTO в этом случае - хорошая идея по нескольким причинам:

  • Уровень представления может видеть только подмножество полей, которые есть у объекта. Вы инкапсулируете сущности
  • Нет связи между серверной частью и фронтентом
  • Если у вас есть бизнес-методы внутри сущностей, но нет в DTO, то добавление DTO означает, что внешний код не может испортить состояние вашей сущности.

введите описание изображения здесь


Сложное корпоративное приложение

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

введите описание изображения здесь

Марцин Шимчак
источник
4

Мы используем одну и ту же модель на сервере и в пользовательском интерфейсе. И это боль. Когда-нибудь мы должны его реорганизовать.

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

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

В конце концов, я думаю, это просто вопрос, какой путь проще. Могут быть проекты, в которых он просто отлично работает и где нет необходимости писать другую модель DTO.

Стефан Штайнеггер
источник
2
Если вы собираетесь использовать привязку данных, запустите запрос linq и привяжите его к анонимному типу. Это позволяет сгладить и изменить иерархию. Вы также можете очень хорошо реализовать фильтрацию и сортировку с этим.
JoshBerke
@ Джош: Спасибо за совет. Это может частично работать. Я сам не программист графического интерфейса и не очень разбираюсь в концепциях графического интерфейса. Проблема будет в тех случаях, когда данные обрабатываются и отправляются обратно на сервер.
Стефан Штайнеггер,
3

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

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

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

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

dkretz
источник
3

Черт возьми, клянусь этой настойчивостью.

В любом случае, это еще один пример того же: закон Парнаса гласит, что модуль должен хранить в секрете, а секрет - это требование, которое может измениться. (У Боба Мартина есть правило, которое является другой версией этого.) В такой системе представление может изменяться независимо от домена . Например, компания, которая поддерживает цены в евро и использует французский язык в офисах компании, но хочет указывать цены в долларах с текстом на китайском языке. Домен одно и то же; презентация может измениться. Итак, чтобы свести к минимуму хрупкость системы, то есть количество вещей, которые необходимо изменить, чтобы реализовать изменение требований, вы разделяете проблемы.

Чарли Мартин
источник
2

Ваша презентация может ссылаться на ваш уровень домена, но не должно быть прямой привязки из вашего пользовательского интерфейса к вашим объектам домена. Объекты домена не предназначены для использования пользовательского интерфейса, поскольку они часто, если они правильно спроектированы, основаны на поведении, а не на представлениях данных. Между пользовательским интерфейсом и доменом должен быть слой сопоставления. MVVM или MVP - хороший образец для этого. Если вы попытаетесь напрямую привязать свой пользовательский интерфейс к домену, вы, вероятно, создадите себе много головной боли. У них две разные цели.

Jlembke
источник
1

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

Теперь предположим, что речевой механизм для системы телефонного звонка работает на компьютере совершенно другого типа (например, Mac), нежели компьютер, на котором работает веб-сайт (возможно, Windows).

Конечно, легко попасть в ловушку: «В моей компании нас интересует только английский язык, наш веб-сайт запускается на LAMP (Linux, Apache, MySQL и PHP), и все используют одну и ту же версию Firefox». Но как насчет 5 или 10 лет?

JonnyBoats
источник
1

С помощью инструмента типа Value Injecter » и концепции «Mappers» на уровне представления при работе с представлениями, гораздо легче понять каждый фрагмент кода. Если у вас есть немного кода, вы не сразу увидите преимущества, но когда ваш проект будет расти все больше и больше, вы будете очень счастливы, работая с представлениями, чтобы вам не приходилось входить в логику сервисов, репозитории, чтобы понять модель представления. View Model - еще один защитник в огромном мире антикоррупционной прослойки, и он на вес золота в долгосрочном проекте.

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

Самуэль
источник
1

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

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

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

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

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

Адриан Томпсон Филлипс
источник
-1

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

Проекты, ориентированные на предметную область, лучше всего работают при использовании в сочетании с ортогональным набором структур функциональных областей (таких как ORM, GUI, Workflow и т. Д.). Всегда помните, что семантика домена должна быть раскрыта только во внешнем слое. Обычно это интерфейс (GUI) и постоянный сервер (RDBM, ORM). Любые эффективно спроектированные промежуточные уровни могут и должны быть инвариантными к предметной области.

alphazero
источник
пункт 1: не создавайте ненужных абстракций (например, повторно используемых компонентов), если только вы не будете использовать их в разных приложениях. параграф 2: Интересно, как общие графические интерфейсы работают во многих разных областях. Реплика: Эта индустрия настолько разрушена, что это уже даже не смешно ...
alphazero