Почему я должен изолировать свои доменные объекты от уровня презентации? - PullRequest
84 голосов
/ 04 мая 2009

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

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

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

  1. Почему мы не должны использовать доменные объекты в нашем уровне представления?
    (если ответ очевиден, «разъединение», то, пожалуйста, объясните, почему это важно в этом контексте)
  2. Должны ли мы использовать дополнительные объекты или конструкции для изоляции наших доменных объектов от интерфейса?

Ответы [ 14 ]

46 голосов
/ 04 мая 2009

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

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

19 голосов
/ 04 мая 2009

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

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

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

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

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

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

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

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

16 голосов
/ 04 мая 2009

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

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

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

13 голосов
/ 14 мая 2009

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

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

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

4 голосов
/ 12 июня 2017

Ответ зависит от масштаба вашей заявки.


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

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

enter image description here


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

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

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

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

enter image description here


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

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

enter image description here

4 голосов
/ 04 мая 2009

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

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

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

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

3 голосов
/ 04 мая 2009

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

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

3 голосов
/ 04 мая 2009

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

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

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

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

2 голосов
/ 02 июня 2009

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

1 голос
/ 14 марта 2016

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

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

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

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

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

...