Я должен отобразить DTO на доменный объект на обеих сторонах клиента и сервера?

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

Мой клиент говорит с сервером только через WCF.

По сути, для каждого доменного объекта у меня есть соответствующий DTO - простое представление, которое содержит только данные - а также класс картопостроителя, который реализует DtoMapper<DTO,Entity> и может преобразовать объект в его эквивалентный DTO или наоборот через статический шлюз:

var employee = Map<Employee>.from_dto<EmployeeDto>();

Сторона сервера этого приложения главным образом о персистентности, где мои DTOs входят от сервиса WCF, десериализовываются, и затем произвольный ORM сохраняет их к базе данных, или запрос запроса входит от WCF, и ORM выполняет тот запрос против DB и возвращает объекты, которые будут сериализированы и переданы обратно WCF.

Учитывая этот сценарий, имеет какой-либо смысл отображать мое хранилище персистентности на доменные объекты, или я должен просто отобразиться непосредственно на DTOs?

Если бы я использую доменные объекты, поток был бы

  1. клиент запрашивает объект
  2. Передачи WCF запрашивают к серверу
  3. ORM запрашивает базу данных и возвращает доменные объекты
  4. доменные объекты преобразовываются в DTOs картопостроителем
  5. WCF сериализирует DTO и возвращается к клиенту
  6. клиент десериализовывает DTO
  7. DTO преобразовывается в доменный объект картопостроителем
  8. созданный viewmodels, и т.д.

подобный в обратной поездке

Если я отображаюсь прямо на DTO, я могу устранить тот, отображающийся на объект на запрос. Что я теряю путем выполнения этого?

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

править:

Я действительно говорил "произвольный ORM" выше, и я действительно хочу вещи быть максимально ORM-and-persistence-agnostic, но если у Вас есть что-либо специальное, чтобы добавить, что это характерно для NHibernate, любой ценой сделайте.

16
задан Jay 6 February 2010 в 20:24
поделиться

6 ответов

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

Подумайте, что такое веб-сервис. Это не просто абстракция над вашим ORM; это контракт . Это общедоступный API для ваших клиентов, как внутренних, так и внешних.

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

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

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


А иногда даже невозможно отправить вашу модель обратно через API. Это может происходить по многим причинам:

  • Циклы в графе объекта. Прекрасно работает в ООП; провальный в сериализации. В конечном итоге вам придется делать болезненный постоянный выбор в отношении того, в каком «направлении» граф должен быть сериализован. С другой стороны, если вы используете DTO, вы можете сериализовать в любом направлении, в каком захотите, что бы ни соответствовало поставленной задаче.

  • Попытка использовать определенные типы механизмов наследования через SOAP / REST может быть в лучшем случае путаницей. Сериализатор XML старого стиля по крайней мере поддерживает xs: choice ; DataContract этого не делает, и я не буду спорить по поводу его обоснования, но достаточно сказать, что у вас, вероятно, есть некоторый полиморфизм в вашей многофункциональной модели предметной области, и чертовски невозможно передать это через веб-службу.

  • Ленивая / отложенная загрузка, которую вы, вероятно, используете, если используете ORM. Достаточно неудобно следить за тем, чтобы он был сериализован должным образом - например, используя объекты Linq to SQL, WCF даже не запускает ленивый загрузчик, он просто помещает null в это поле, если вы не загрузите его вручную - но проблема еще больше усугубляется с возвращением данных.Что-то столь же простое, как автоматическое свойство List , которое инициализируется в конструкторе - достаточно распространенное в модели предметной области - просто не работает в WCF, потому что оно не вызывает ваш конструктор. Вместо этого вы должны добавить метод инициализатора [OnDeserializing] , и вы действительно не хотите загромождать свою модель предметной области этим мусором.

  • Я также только что заметил в скобках замечание о том, что вы используете NHibernate. Учтите, что интерфейсы типа IList вообще не могут быть сериализованы через веб-службу! Если вы используете классы POCO с NHibernate, как это делает большинство из нас, то это просто не сработает, точка.


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

  • Информация об учетной записи (номер счета, имя и т. Д.)
  • Данные, относящиеся к счету-фактуре (номер счета-фактуры, дата, срок оплаты и т. Д.)
  • Информация уровня A / R ( предыдущий баланс, просроченные платежи, новый баланс)
  • Информация о продукте или услуге для всего, что указано в счете;
  • И т. д.

Это, вероятно, прекрасно вписывается в модель предметной области. Но что, если клиент хочет создать отчет, в котором будет отображаться 1200 таких счетов? Какой-то отчет о согласовании?

Это отстой для сериализации. Теперь вы отправляете 1200 счетов-фактур с одними и теми же данными, сериализуемыми снова и снова - те же учетные записи, те же продукты, тот же A / R.Внутренне ваше приложение отслеживает все ссылки; он знает, что Счет-фактура № 35 и Счет-фактура № 45 предназначены для одного и того же клиента и, следовательно, имеют ссылку на Заказчик ; вся эта информация теряется при сериализации, и вы в конечном итоге отправляете невероятное количество избыточных данных.

На самом деле вам нужно отправить специальный отчет, который включает:

  • Все учетные записи, включенные в отчет, и их A / R;
  • Все продукты, включенные в отчет;
  • Все счета, только с идентификаторами продукта и аккаунта.

Вам необходимо выполнить дополнительную «нормализацию» исходящих данных, прежде чем отправлять их клиенту, если вы хотите избежать массивной избыточности. Это сильно способствует подходу DTO; нет смысла иметь такую ​​структуру в вашей модели предметной области, потому что ваша модель предметной области уже по-своему заботится об избыточности.

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

25
ответ дан 30 November 2019 в 17:39
поделиться

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

Абстрактные слои хороши не только для валидации, но и для того, чтобы изолировать ваш код от изменений ниже/выше него, и сделать ваш код более тестируемым и с меньшим количеством повторений.

Также, если вы не заметите большого узкого места в дополнительном преобразовании, помните: ранняя оптимизация является корнем всего зла. :)

.
2
ответ дан 30 November 2019 в 17:39
поделиться

Когда вы говорите, что ваше серверное приложение «в основном» ориентировано на постоянство, я думаю, что это ключевая вещь, о которой нужно подумать. Действительно ли существует модель домена на стороне сервера, которая требует некоторого интеллекта в отношении данных, которые она получает, или ваша служба WCF выступает исключительно в качестве шлюза между вашей моделью домена и хранилищем данных?

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

(DB-> ORM-> EmployeeEntity-> Client1DTOAssembler-> Client1EmployeeDTO).

1
ответ дан 30 November 2019 в 17:39
поделиться

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

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

Эти же DTO затем десериализуются на клиенте, и оттуда вы можете напрямую отображать их на свои модели UIViewModels. Таким образом, общая картина выглядит примерно так:

  • Клиент запрашивает объект по идентификатору из службы WCF
  • Служба WCF получает объект из репозитория / ORM
  • Использует AutoMapper для сопоставления объекта с DTO
  • Клиент получает DTO
  • ] Использует AutoMapper для отображения на UI ViewModel
  • UIViewModel привязан к GUI
2
ответ дан 30 November 2019 в 17:39
поделиться

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

В нашем случае наш клиент и сервер не используют повторно сборку, содержащую «DTO». Это дает нам возможность просто добавлять код к частичным классам, сгенерированным ссылкой на службу, поэтому мы часто можем использовать DTO как есть на стороне клиента и рассматривать его как объект домена. В других случаях у нас могут быть объекты домена только на стороне клиента, которые служат фасадами для группы постоянных объектов, которые мы получили от службы WCF.

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

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

На стороне клиента может не потребоваться дополнительное сопоставление, если вы можете просто украсить или расширить свои DTO, и это довольно простое решение.

1
ответ дан 30 November 2019 в 17:39
поделиться

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

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

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

0
ответ дан 30 November 2019 в 17:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: