Рекомендации по сопоставлению объекта DTO с доменом?

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

Ситуация:

У нас много доменных объектов. Мы используем модель CSLA, поэтому наши доменные объекты могут быть довольно сложными и содержать собственный доступ к данным. Вы не хотите передавать их по проводам. Мы собираемся написать несколько новых сервисов, которые будут возвращать данные в нескольких форматах (.Net, JSON и т.д.). По этой (и другим) причинам мы также создаем компактный объект передачи данных для передачи по проводам.

Мой вопрос: как должны быть связаны объект DTO и домен?

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

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

Есть ли другие способы? Стоит ли использовать один из вышеперечисленных способов? Если так или нет, то почему?

Ответ 1

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

Лично я пытаюсь сохранить отображение из своих доменных объектов и возложить ответственность на то, что я называю "Уровень менеджера/службы". Это слой, который находится между приложением и репозиторием (-ями), и обеспечивает бизнес-логику, такую ​​как координация рабочего процесса (если вы изменяете A, вам, возможно, придется также изменить B, поэтому служба A будет работать с сервисом B).

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

Ответ 3

Мы используем шаблоны T4 для создания классов сопоставления.

Pro - доступный для человека код, доступный во время компиляции, быстрее, чем среда выполнения. 100% контроль над кодом (можно использовать частичные методы/шаблон шаблона для расширения функциональности на разовой основе)

Con - исключение определенных свойств, коллекций объектов домена и т.д., изучение синтаксиса T4.

Ответ 4

Как вы видите, чтобы реализовать конструктор внутри класса DTO, который принимает в качестве параметра объект домена?

Скажи... Что-то вроде этого

class DTO {

     // attributes 

     public DTO (DomainObject domainObject) {
          this.prop = domainObject.getProp();
     }

     // methods
}

Ответ 5

Другое возможное решение: http://glue.codeplex.com.

Особенности:

  • Двунаправленное отображение
  • Автоматическое сопоставление
  • Сопоставление между различными типами
  • Вложенное сопоставление и сглаживание
  • Списки и массивы
  • Проверка отношений
  • Тестирование отображения
  • Свойства, поля и методы

Ответ 6

Вы также можете попробовать Otis, сопоставление объектов с объектами. Понятия похожи на сопоставление NHibernate (атрибут или XML).

http://code.google.com/p/otis-lib/wiki/GettingStarted

Ответ 7

Я могу предложить инструмент, который я создал, и с открытым исходным кодом размещен в CodePlex: EntitiesToDTOs.

Сопоставление с DTO на Entity и наоборот реализуется с помощью методов расширения, они составляют сторону Ассемблера каждого конца.

Вы заканчиваете следующим кодом:

Foo entity = new Foo();
FooDTO dto = entity.ToDTO();
entity = dto.ToEntity();

List<Foo> entityList = new List<Foo>();
List<FooDTO> dtoList = entityList.ToDTOs();
entityList = dtoList.ToEntities();

Ответ 8

Почему бы нам не сделать так?

class UserDTO {
}

class AdminDTO {
}

class DomainObject {

 // attributes
 public DomainObject(DTO dto) {
      this.dto = dto;
 }     

 // methods
 public function isActive() {
      return (this.dto.getStatus() == 'ACTIVE')
 }

 public function isModeratorAdmin() {
      return (this.dto.getAdminRole() == 'moderator')
 }

}


userdto = new UserDTO();
userdto.setStatus('ACTIVE');

obj = new DomainObject(userdto)
if(obj.isActive()) {
   //print active
}

admindto = new AdminDTO();
admindto.setAdminRole('moderator');

obj = new DomainObject(admindto)
if(obj.isModeratorAdmin()) {
   //print some thing
}

@FrederikPrijck (или) кто-то: предложите. В приведенном выше примере DomainObject зависит от DTO. Таким образом, я могу избежать того, чтобы код отображал объект dto ↔ domainobject.

или класс DomainObject может расширять класс DTO?

Ответ 9

Другим вариантом будет использование ModelProjector. Он поддерживает все возможные сценарии и очень прост в использовании с минимальными затратами.

Ответ 10

Для этого мы можем использовать шаблон Factory, Memento и Builder. Фабрика скрывает подробности о том, как создать экземпляр модели домена из DTO. Memento позаботится о сериализации/десериализации модели домена в/из DTO и может даже получить доступ к частным членам. Builder позволяет отображать DTO в домены с удобным интерфейсом.

Ответ 11

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

Хранение карт в другом месте было бы единственной альтернативой - но куда это должно пойти? Я пытался представить картографические объекты/сервисы, но, после всего сказанного и сделанного, это выглядело как переобучение (и, вероятно, было). Я имел некоторый успех, используя Automapper и такие для небольших проектов, но такие инструменты, как Automapper, имеют свои собственные подводные камни. У меня было довольно трудно найти проблемы, связанные с отображениями, потому что отображения Automapper неявны и полностью отделены от остальной части вашего кода (не как "разделение интересов", а скорее как "где живёт забытое Богом отображение"), поэтому они иногда бывает трудно отследить. Нельзя сказать, что Automapper не имеет своего использования, потому что это делает. Я просто думаю, что картирование должно быть как можно более очевидным и прозрачным, чтобы избежать проблем.

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