Какая польза от DTO вместо Entity?

18

Я работаю над приложением RCP, я новичок в этом приложении.

Spring bean-компоненты используются для написания бизнес-логики для сохранения / извлечения сущностей.

Но вместо того, чтобы отправлять объекты напрямую клиенту, мы конвертируем их в DTO и заполняем клиента. Сохраняя, мы снова конвертируем DTO в сущность и сохраняем.

В чем выгода этих конверсий? Может кто-нибудь объяснить?

Навин Кочерла
источник
What's the benefit of these conversions?отделение модели постоянства данных от модели данных (представления), предлагаемой потребителям. Преимущества развязки широко обсуждались в SE. Однако цель DTO - собрать в одном ответе столько информации, сколько необходимо клиентам для сохранения вызовов на сервере. Что делает общение клиент-сервер более гладким.
Laiv
Ваш пример хорош Когда вы являетесь клиентом (взгляды ...), больно менять, но самая большая проблема в том, что в системе уже есть интеграция с 3-й стороной, которую невозможно изменить (контракт, сборы ...). Если ваша система будет иметь стороннюю интеграцию, используйте DTO.
Лукас Гонсалвеш

Ответы:

44

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


Все примеры будут основаны на этой простой модели данных:

У Personобъекта есть пять свойств:Id, FirstName, LastName, Age, CityId

И вы можете предположить, что приложение использует эти данные различными способами (отчеты, формы, всплывающие окна, ...).

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


Пример 1 - Изменение базовой структуры данных - без DTO

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

Поскольку вам больше не нужно хранить Ageзначение локально, его необходимо удалить из Personсущности. Здесь важно понимать, что сущность представляет данные базы данных , и ничего более. Если это не в базе данных, это не в сущности.
Когда вы извлекаете возраст из правительственного веб-сервиса, он будет храниться в другом объекте (или int).

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


Пример 2 - Изменение базовой структуры данных - с DTO

В старой системе, есть также PersonDTOобъект с теми же пятью свойствами: Id, FirstName, LastName, Age, CityId. После получения a Person, сервисный уровень преобразует его в a PersonDTOи затем возвращает его.

Но теперь требования изменились. Возраст человека должен быть динамически извлечен из правительственной базы данных (предположим, основываясь на его имени и фамилии).

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

Однако, поскольку у вас есть посредник PersonDTO, важно видеть , что этот класс может держать в Ageсобственности. Сервисный уровень извлекает Person, преобразует его в a PersonDTO, затем он также извлекает возраст человека из правительственного веб-сервиса, сохраняет это значение PersonDTO.Ageи передает этот объект.

Важной частью здесь является то, что любой, кто использует уровень обслуживания, не видит разницы между старой и новой системой . Это включает в себя ваш интерфейс. В старой системе он получил полный PersonDTOобъект. И в новой системе он по-прежнему получает полный PersonDTOобъект. Представления не должны быть обновлены .

Вот что мы имеем в виду, когда используем фразу разделения интересов : есть две разные проблемы (хранение данных в базе данных, представление данных во внешнем интерфейсе), и каждый из них нуждается в различном типе данных. Даже если эти два типа данных сейчас содержат одни и те же данные, это может измениться в будущем.
В данном примере Ageесть различие между двумя типами данных: Person(объект базы данных) не нуждается Age, но PersonDTO(тип данных внешнего интерфейса) действительно нуждается в этом.
Отделяя задачи (= создавая отдельные типы данных) с самого начала, кодовая база намного более устойчива к изменениям, внесенным в модель данных.

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

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


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

Подвести итоги

  • Отдельные обязанности (проблемы) должны работать отдельно друг от друга. Они не должны совместно использовать какие-либо ресурсы, такие как классы данных (например Person)
  • Тот факт, что сущность и ее DTO имеют одинаковые свойства, не означает, что вам нужно объединить их в одну сущность. Не срезайте углы.
    • В качестве более наглядного примера, скажем, наша база данных содержит страны, песни и людей. Все эти объекты имеют Name. Но только то, что они имеют Nameсвойство, не означает, что мы должны заставить их наследовать от общего EntityWithNameбазового класса. Различные Nameсвойства не имеют никакого значимого отношения.
    • Если одно из свойств когда-либо изменится (например, песня Nameбудет переименована Title, или человек получит « FirstNameа» LastName), им придется потратить больше усилий, чтобы отменить наследство, которое вам даже не нужно .
    • Хотя это не так очевидно, ваш аргумент, что вам не нужен DTO, когда у вас есть сущность, тот же. Вы смотрите на сейчас , но вы не готовитесь к будущим изменениям. ЕСЛИ сущность и DTO абсолютно одинаковы, и ЕСЛИ вы можете гарантировать, что никогда не произойдет никаких изменений в модели данных; тогда вы правы, что можете опустить DTO. Но дело в том, что вы никогда не сможете гарантировать, что модель данных никогда не изменится.
  • Хорошая практика не всегда окупается сразу. Это может начать окупаться в будущем, когда вам нужно будет вернуться к старому приложению.
  • Основной убийцей существующих кодовых баз является ухудшение качества кода, что постоянно усложняет поддержание кодовой базы, пока не превратится в бесполезный беспорядок кода спагетти, который не поддерживается.
  • Надлежащая практика, такая как реализация разделения проблем от начала работы, направлена ​​на то, чтобы избежать этого скользкого пути плохого обслуживания, чтобы поддерживать кодовую базу как можно дольше поддерживаемой.

Как правило, при рассмотрении разделения проблем думайте об этом следующим образом:

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

В хорошо разделенной кодовой базе изменение конкретной задачи должно обрабатываться только одним человеком:

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

Если бы все эти разработчики использовали одну Personи ту же сущность и в нее были внесены незначительные изменения, все должны были бы участвовать в этом процессе.

Но используя отдельные классы данных для каждого слоя, эта проблема не так распространена:

  • Пока разработчик базы данных может возвращать действительный PersonDTOобъект, бизнес и пользовательский интерфейс не заботятся о том, что он изменил способ хранения / извлечения данных.
  • Пока бизнес-разработчик хранит данные в базе данных и предоставляет необходимые данные внешнему интерфейсу, разработчикам базы данных и пользовательского интерфейса все равно, решил ли он переделать свои бизнес-правила.
  • Пока пользовательский интерфейс может быть разработан на основе PersonViewModel, пользовательский интерфейс может создавать пользовательский интерфейс так, как он хочет. Разработчикам баз данных и бизнесу все равно, как это делается, поскольку это не влияет на них.

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

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

Flater
источник
Комплексный ответ, спасибо. У меня есть вопросы; Ценю, если вы отвечаете: 1 - Поправьте меня, если я не прав. Бизнес-объект или объект- представление предназначены для передачи данных между уровнем представления и бизнес-уровнем, а объект-сущность предназначен для передачи данных между бизнес-уровнем и уровнем доступа к данным . и DTO можно использовать как тупой BO. 2 - Предположим, что двум представлениям нужна разная информация о компании, тогда нам нужны две разные компании DTO?
Араш
1
@Arash (1) «DTO» действительно всеобъемлющее определение для любого класса данных, который используется для обмена между двумя уровнями. Бизнес-объект и объект представления являются DTO. (2) Это во многом зависит от многих вещей. Создание нового dto для каждой требуемой коллекции полей - сложная задача. По сути, нет ничего плохого в том, чтобы просто возвращать полное DTO компании (где это разумно), а затем позволять представлению выбирать области, в которых он заинтересован. Это вопрос поиска баланса между осуществлением адекватного разделения интересов и избеганием чрезмерного обучения и бессмысленного повторения.
Флейтер
Теперь это имеет смысл для меня. Большое спасибо. Flater.
Араш
Еще один вопрос. Вы сказали "бизнес-объект и объект просмотра". Я думал, что оба равны. Когда я искал, я понял, что Business Object имеет общее значение для сравнения с View Object . Но бизнес-объект должен быть получен из варианта использования, а объектный объект должен быть получен из модели данных, поэтому они разные, я прав? не могли бы вы объяснить это немного, пожалуйста?
Араш
@Arash: Разница между тем, что вы называете «бизнес-объектом» и «объектом просмотра», заключается в контексте . Для нас, людей, это различие важно для правильного понимания вещей. Но компилятор (и, соответственно, сам язык) не видит технической разницы между ними. Когда я говорю, что они одинаковы, я имею в виду это с технической точки зрения. Оба являются просто классом со свойствами, предназначенными для хранения данных и их передачи. В этом отношении между ними нет разницы.
Flater