что такое dto java
DTO vs POCO vs Value Object
Определения DTO, POCO и Value Object
Вначале небольшая ремарка по поводу Value Object. В C# существует похожая концепция, называемая Value Type. Это всего лишь деталь имплементации того, как объекты хранятся в памяти и мы не будем касаться этого. Value Object, о котором пойдет речь, — понятие из среды DDD (Domain-Driven Design).
Ок, давайте начнем. Вы возможно заметили, что такие понятия как DTO, Value Object и POCO часто используются как синонимы. Но действительно ли они означают одно и то же?
DTO — это класс, содержащий данные без какой-либо логики для работы с ними. DTO обычно используются для передачи данных между различными приложениями, либо между слоями внутри одного приложения. Их можно рассматривать как хранилище информации, единственная цель которого — передать эту информацию получателю.
С другой стороны, Value Object — это полноценный член вашей доменной модели. Он подчиняется тем же правилам, что и сущности (Entities). Единственное отличие между Value Object и Entity в том, что у Value Object-а нет собственной идентичности. Это означает, что два Value Object-а с одинаковыми свойствами могут считаться идентичными, в то время как две сущности отличаются друг от друга даже в случае если их свойства полностью совпадают.
Value Object-ы могут содержать логику и обычно они не используются для передачи информации между приложениями.
POJO был представлен Мартином Фаулером в качестве альтернативы для JavaBeans и других «тяжелых» enterprise-конструкций, которые были популярны в ранних 2000-х.
Основной целью POJO было показать, что домен приложения может быть успешно смоделирован без использования JavaBeans. Более того, JavaBeans вообще не должны быть использованы для этой цели.
Другой хороший пример анти-POCO подхода — Entity Framework до версии 4.0. Каждый класс, сгенерированный EF, наследовал от EntityObject, что привносило в домен логику, специфичную для EF. Начиная с версии 4, Entity Framework добавил возможность работать с POCO моделью — возможность использовать классы, которые не наследуются от EntityObject.
Таким образом, понятие POCO означает использование настолько простых классов насколько возможно для моделирования предметной области. Это понятие помогает придерживаться принципов YAGNI, KISS и остальных best practices. POCO классы могут содержать логику.
Корреляция между понятиями
Есть ли связи между этими тремя понятиями? В первую очередь, DTO и Value Object отражают разные концепции и не могут использоваться взаимозаменяемо. С другой стороны, POCO — это надмножество для DTO и Value Object:
Другими словами, Value Object и DTO не наследуют никаким сторонним компонентам и таким образом являются POCO. В то же время, POCO — это более широкое понятие: это может быть Value Object, Entity, DTO или любой другой класс в том случае если он не наследует компонентам, не относящимся напрямую к решаемой вами проблеме.
Вот свойства каждого из них:
Заметьте, что POCO-класс может и иметь, и не иметь собственной идентичности, т.к. он может быть как Value Object, так и Entity. Также, POCO может содержать, а может и не содержать логику внутри себя. Это зависит от того, является ли POCO DTO.
Заключение
Вышесказанное в статье можно суммировать следующим образом:
DTO в JS
Информационные системы предназначены для обработки данных, а DTO (Data Transfer Object) является важным концептом в современной разработке. В “классическом” понимании DTO являются простыми объектами (без логики), описывающими структуры данных, передаваемых “по проводам” между разнесенными процессами (remote processes). Зачастую данные «по проводам» передаются в виде JSON.
Если DTO используются для передачи данных между слоями приложения (база данных, бизнес-логика, представления), то, по Фаулеру, это называется LocalDTO. Некоторые разработчики (включая самого Фаулера) негативно относятся к локальным DTO. Основным отрицательным моментом локального применения DTO является необходимость маппинга данных из одной структуры в другую при их передаче от одного слоя приложения к другому.
Тем не менее, DTO являются важным классом объектов в приложениях и в этой статье я покажу JS-код, который на данный момент считаю оптимальным для DTO (в рамках стандартов ECMAScript 2015+).
Структура данных
Во-первых, в коде должна быть отражена сама структура данных. Лучше всего это делать с использованием классов (аннотации JSDoc помогают ориентироваться в типах данных):
Это пример простой структуры данных, где каждый атрибут является примитивом. Если некоторые атрибуты сами являются структурами, то класс выглядит примерно так:
Создание объектов
Как правило, создание экземпляра DTO в половине случаев связано разбором имеющейся структуры данных, полученной «по проводам» с «другой стороны». Поэтому конструктор DTO получает на вход некоторый JS-объект, из которого пытается извлечь знакомые ему данные:
В конструкторе структуры со сложными атрибутами используются конструкторы для соответствующих атрибутов:
Если какой-то атрибут представляет из себя массив, то в конструкторе его разбор выглядит примерно так:
Если какие-то данные должны быть сохранены в атрибуте без разбора, то это тоже возможно (хотя к DTO имеет такое себе отношение):
Метаданные
Например, при выборке данных из БД:
Резюме
Если сводить воедино все три составляющих DTO (структура, конструктор, метаданные), то получается примерно такой es-модуль:
Подобный подход позволяет извлекать знакомые подструктуры данных из больших структур (конфигурационных файлов, ответов от сервисов и т.п.), непосредственно относящиеся к текущему контексту, а IDE, за счёт аннотаций, имеет возможность помогать разработчику ориентироваться в этой подструктуре.
Послесловие
Что привлекло внимание. Во-первых, двухступенчатое создание объекта:
Во-вторых, значения по-умолчанию вешаются на прототип, используемый для создания экземпляров класса:
Это опять-таки направлено на минимизацию потребления памяти при массовом создании экземпляров.
В третьих, отсутствуют метаданные об используемых в DTO атрибутах:
Наверное, с точки зрения разрабов этого генератора кода такая информация показалась им излишней и при необходимости её вполне можно добавить в генератор.
В общем, на мой взгляд, вполне неплохое совпадение изложенного в моей публикации (структура, конструктор, метаданные) с практикой (структура, конструктор). Что касается статических методов, то Safary только 26-го апреля этого года научилась понимать статику (хотя того же эффекта можно добиться за счёт прямого переноса методов в класс: ConfigEmail.initialize = function(obj)<> ).
Коллега @nin-jin в своём комментарии справедливо заметил, что хорошо бы «проверять типы полей перед их сохранением«. Я могу согласиться, что было бы правильным приводить типы данных к ожидаемым (нормализовать данные). Кстати, так и делается в OpenAPI-генераторе:
Насколько я понял, функция Rec предназначена для создания простых дата-объектов (объекты без логики, только с данными) «на лету». В общем-то это и есть DTO, только режима runtime, а не уровня кода (те же метаданные в моём примере позволяют программисту «метить» места использования соотв. DTO и находить их без запуска приложения).
Этот абзац я вставил в начало публикации специально для того, чтобы при сохранении он перенесся в конец текста, а второй абзац стал первым. Вот такая занимательная бага есть сегодня на Хабре.
Какой смысл использовать DTO (объекты передачи данных)?
Какой смысл использовать DTO и является ли это устаревшей концепцией? Я использую POJO в слое представления для передачи и сохранения данных. Могут ли эти POJO рассматриваться как альтернатива DTO?
DTO является шаблоном и не зависит от реализации (POJO / POCO). DTO говорит, что, поскольку каждый вызов любого удаленного интерфейса стоит дорого, ответ на каждый вызов должен принести как можно больше данных. Таким образом, если требуется несколько запросов для доставки данных для конкретной задачи, данные, которые должны быть доставлены, могут быть объединены в DTO, так что только один запрос может принести все необходимые данные. Каталог шаблонов архитектуры корпоративных приложений содержит более подробную информацию.
DTO как концепция (объекты, целью которых является сбор данных, возвращаемых клиенту сервером), безусловно, не устарела.
Однако причина, по которой это понятие устарело, а не просто неправильно, состоит в том, что некоторые (в основном более старые) фреймворки / технологии требуют этого, поскольку их модели предметной области и представления не являются POJOS и вместо этого напрямую связаны с фреймворком.
Хотя DTO не является устаревшим шаблоном, он часто применяется без необходимости, что может сделать его устаревшим.
Например, скажем, у вас есть JSF ManagedBean. Общий вопрос заключается в том, должен ли бин содержать ссылку на сущность JPA напрямую или он должен поддерживать ссылку на некоторый промежуточный объект, который впоследствии преобразуется в сущность. Я слышал, что этот промежуточный объект называется DTO, но если ваши ManagedBeans и сущности работают в одной и той же JVM, то использование шаблона DTO дает мало преимуществ.
Рассмотрим аннотации Bean Validation. Ваши сущности JPA, вероятно, снабжены валидациями @NotNull и @Size. Если вы используете DTO, вам нужно будет повторить эти проверки в DTO, чтобы клиентам, использующим ваш удаленный интерфейс, не нужно было отправлять сообщение, чтобы узнать, что они не прошли базовую проверку. Представьте себе всю эту дополнительную работу по копированию аннотаций Bean Validation между вашим DTO и Entity, но если ваш View и Entities работают в одной и той же JVM, нет необходимости выполнять эту дополнительную работу: просто используйте Entities.
Ссылка IAmTheDude на Каталог шаблонов архитектуры корпоративных приложений предоставляет краткое объяснение DTO, и вот еще несколько ссылок, которые я нашел освещающими:
Не самые очевидные советы по написанию DTO на Java
Сегодня приложения зачастую имеют распределенный характер. Для подключения к другим сервисам нужно писать больше кода — и при этом стараться сделать его простым.
Чтобы воспользоваться данными из внешней службы, мы обычно преобразуем полезную нагрузку JSON в объект передачи данных (Data Transfer Object, DTO). Код, обрабатывающий DTO, быстро усложняется, но с этим могут помочь несколько советов. Вполне возможно писать DTO, с которыми легче взаимодействовать и которые облегчают написание и чтение кода. Если объединить их вместе — можно упростить себе работу.
Сериализация DTO “по учебнику”
Начнем с типичного способа работы с JSON. Вот структура JSON. Этот JSON представляет пиццу “Реджина”.
PizzaDto — «старый добрый Java-объект», POJO: объект со свойствами, геттерами, сеттерами и всем остальным. Он отражает структуру JSON, поэтому преобразование между объектом и JSON занимает всего одну строку. Вот пример этого с библиотекой Jackson:
Преобразование простое и прямолинейное. В чем же тогда проблема?
В реальной жизни DTO бывают довольно сложными. Код для создания и инициализации DTO может включать вплоть до десятков строк. Иногда больше. Это проблема, потому что сложный код содержит больше ошибок и менее чувствителен к изменениям.
Моя первая попытка упростить создание DTO — воспользоваться неизменяемым DTO: таким, который нельзя модифицировать после создания.
Такой подход может показаться странным, если вы не знакомы с этой идеей, поэтому давайте сосредоточимся на ней поподробнее.
Создание неизменяемых DTO
Если говорить просто, то объект неизменяемый, если его состояние не может поменяться после сборки.
У неизменяемого объекта нет сеттера. Все его свойства — окончательные и должны быть инициализированы при построении.
Это важно, потому что пицца “Реджина” без грибов — уже определенно не пицца “Реджина”.
Если серьезнее, то Джошуа Блох, автор книги “Java: эффективное программирование”, дает такую рекомендацию для создания неизменяемых классов:
“Если в вашем классе есть какие-либо поля, которые ссылаются на изменяемые объекты, убедитесь, что клиенты класса не могут получать ссылки на эти объекты”. — Джошуа Блох
Если какое-либо свойство вашего DTO является изменяемым, вам необходимо сделать защитные копии. С их помощью вы предотвратите модификацию вашего DTO извне.
Примечание: начиная с Java 16, существует более краткий способ создания неизменяемых классов через записи.
Хорошо. Теперь у нас есть неизменяемый DTO. Но как это упрощает код?
Преимущества неизменяемости
Неизменяемость приносит много преимуществ, но вот мое любимое: неизменяемые переменные не имеют побочных эффектов.
Рассмотрим на примере. В этом фрагменте кода есть ошибка:
После выполнения этого кода пицца не содержит ожидаемого состояния. Какая строка вызвала проблему?
Попробуем два ответа: сначала с изменяемой переменной, а затем с неизменяемой.
С неизменяемыми переменными отладка становится проще. Но это еще не все.
Поскольку pizza — неизменяемый объект, verify() не может просто исправить его. Придется создавать и возвращать измененную пиццу, а клиентский код необходимо адаптировать:
В этой новой версии очевидно, что метод verify() возвращает новую исправленную пиццу. Неизменяемость делает код более понятным. Его становится легче читать и легче развивать.
Есть и другие преимущества В своей книге Джошуа Блох просто рекомендует “минимизировать изменчивость”.
“Неизменяемые классы проще проектировать, реализовывать и использовать, чем изменяемые классы. Они менее подвержены ошибкам и более безопасны”. — Джошуа Блох
Теперь возникает интересный вопрос: можем ли мы поступать так же с DTO?
Неизменяемые DTO… А это осмысленно?
Цель DTO — передача данных между процессами. Объект инициализируется, а затем его состояние не должно меняться. Либо он будет сериализован в JSON, либо будет использоваться клиентом. Это делает неизменность естественной. Неизменяемый DTO будет передавать данные между процессами с гарантией.
Как оказалось, это не соответствует истине.
Неизменяемые DTO с Jackson
Jackson — самая распространенная JSON-библиотека для Java.
Когда у DTO есть геттеры и сеттеры, Jackson может сопоставить объект с JSON без какой-либо дополнительной настройки. Но с неизменяемыми объектами Jackson нуждается в небольшой помощи. Ему нужно знать, как собирать объект.
Вот и все. Теперь нас есть неизменяемый DTO, который Jackson может преобразовать в JSON и обратно в объект.
Неизменяемые DTO с Gson и Moshi
Есть две альтернативы Jackson: Gson и Moshi.
С помощью этих библиотек еще проще преобразовать JSON в неизменяемый DTO, потому что им не нужны никакие дополнительные аннотации.
Но почему Jackson вообще требует аннотаций, в отличие от Gson и Moshi?
Никакой магии. Дело в том, что, когда Gson и Moshi генерируют объект из JSON, они создают и инициализируют его путем отражения. Кроме того, они не задействуют конструкторы.
Я не большой поклонник такого подхода. Он вводит в заблуждение, потому что разработчик может вложить некоторую логику в конструктор и никогда не узнать, что он не вызывается. По сравнению с этим, Jackson представляется гораздо более безопасным.
Избегайте нулевых значений
У Jackson есть еще одно преимущество. Если поместить в конструктор некоторую логику, он будет вызываться всегда, независимо от того, создан ли DTO кодом приложения или сгенерирован из JSON.
Можно воспользоваться этим преимуществом для избегания значений null и улучшить конструктор для инициализации полей с ненулевыми значениями.
В приведенном ниже фрагменте кода поля инициализируются пустыми значениями, когда входные данные равны нулю.
Так вы напишете меньше кода и повысите надежность. Что может быть лучше?
И последнее по счету, но не по важности: создавайте DTO со строителями
Есть еще один совет, как упростить инициализацию DTO. В комплекте с каждым DTO я создаю Builder. Он предоставляет свободный API для облегчения инициализации DTO.
Вот пример создания PizzaDto через сборщик:
С помощью сложных DTO разработчики делают код более выразительным. Этот шаблон настолько великолепен, что Джошуа Блох почти начинает с него свою книгу “Java: эффективное программирование”.
“Такой клиентский код легко писать и, что более важно, читать”. — Джошуа Блох
Вот пример для PizzaDto :
Некоторые пользуются Lombok для создания конструкторов во время компиляции. Это упрощает DTO.
Я предпочитаю генерировать код конструктора с помощью плагина Builder generator IntelliJ. Затем можно добавить перегрузки методов, как в предыдущем фрагменте кода. Конструктор таким образом становится более гибким, а клиентский код — более компактным.
Заключение
Вот основные советы, которые я держу в голове при написании DTO. Соединенные вместе, они действительно улучшат ваш код. Кодовая база становится легче для чтения, проще в обслуживании и, в конечном счете, так проще делиться ею с вашей командой.
Шаблон объекта передачи данных в Java – Реализация и сопоставление
В этом руководстве мы реализуем шаблон объекта передачи данных в приложении Java Spring Boot. Мы также рассмотрим примеры сопоставления сущностей с DTO.
Вступление
Корпоративное приложение-это программное решение, созданное для нужд организации. Часто это крупномасштабная, многоуровневая, масштабируемая система. Корпоративное программное обеспечение может работать с большим количеством сложных данных, и для этого типа программного обеспечения важно иметь хорошую архитектуру.
Шаблоны архитектуры корпоративных приложений являются стандартизированными решениями распространенных проблем, возникающих в больших системах. Они развивают архитектурное мышление и помогают разработчикам быть более уверенными в создании систем с доказанной надежностью.
Корпоративным приложениям может быть поручено управлять, отображать или хранить огромные объемы данных. При работе с этими приложениями не следует забывать о том, чтобы избегать тесной связи и обеспечивать целостность/безопасность данных.
Объект Передачи Данных
DTO могут содержать либо все данные из источника, либо частичные данные. Они также могут содержать данные из одного или нескольких источников. При внедрении, чтобы стать средством передачи данных между системами.
Что стоит отметить, так это то, что применение шаблона объекта передачи данных может стать анти-шаблоном в локальных системах. Он предназначен для использования в удаленных вызовах для повышения безопасности и ослабления связи. Если применить это к локальным системам, то это просто перепроектирование простой функции.
Мотивация
Давайте предположим, что нам нужно разработать корпоративную систему для компании. Система будет включать базу данных с различной общей информацией о сотрудниках – зарплата, проекты, сертификаты, личные данные (адрес, семейное положение, номер телефона и т.д.).
Охрана на входе в компанию требует доступа к нашей системе, чтобы идентифицировать работника, который хочет войти. Им нужна какая-то элементарная информация, такая как фамилия и фотография работника.
Мы не хотим отправлять в систему безопасности другую конфиденциальную информацию, такую как личная информация. Это избыточно и подвергает канал связи между системами атакам. Мы предоставим только то, что необходимо, и объем данных будет определен в объявлении.
В приложениях Java – мы используем классы сущностей для представления таблиц в реляционной базе данных. Без DTO нам пришлось бы предоставлять все объекты удаленному интерфейсу. Это приводит к сильной связи между API и моделью сохранения.
Используя DTO для передачи только необходимой информации, мы ослабляем связь между API и нашей моделью, что позволяет нам легче поддерживать и масштабировать сервис.
Реализация объекта передачи данных
Давайте создадим приложение, которое позаботится об отслеживании местоположения ваших друзей. Мы создадим приложение Spring Boot, которое предоставляет API REST. Используя его, мы сможем извлекать местоположения пользователей из базы данных H2.
Настройка пружинной загрузки
Самый простой способ начать с чистого приложения Spring Boot-это использовать Spring Initializr :
Кроме того, вы также можете использовать интерфейс командной строки Spring Boot для начальной загрузки приложения:
Если у вас уже есть приложение Maven/Spring, добавьте зависимость в свой pom.xml файл:
Или если вы используете Gradle:
Демонстрационное приложение
Давайте начнем с Пользователя модели:
На этом этапе мы хотели бы создать контроллер, который обрабатывает запрос GET и возвращает список местоположений пользователя. Хотя, если мы извлекем Пользователя и Местоположение объекты из нашей базы данных и просто напечатаем необходимую информацию – другая информация, такая как пароль, также будет содержаться в этом объекте. Мы не будем его печатать, но оно там будет.
Git Essentials
Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!
Теперь этот объект содержит всю информацию, которую мы хотим показать конечному пользователю. Теперь нам понадобится способ сопоставить Пользователя и Местоположение объектов в одно Местоположение пользователя D ДЛЯ объекта. Обычно это делается с помощью инструментов отображения, таких как MapStruct или ModelMapper , которые мы рассмотрим в последних разделах.
Давайте загрузим нашу базу данных некоторой фиктивной информацией для целей тестирования:
Как проверить нашу конечную точку, мы будем использовать такой инструмент, как Postman, чтобы попасть в наши конечные точки:
Отлично! Список наших пользователей возвращается только с необходимой информацией, как переданной, так и отображенной.
Сопоставление с помощью ModelMapper
ModelMapper – это отличная библиотека картографирования, которая позволяет нам сопоставлять модели и DTO. Это упрощает сопоставление объектов, автоматически определяя, как одна объектная модель сопоставляется с другой.
Чтобы добавить его в проект Maven, мы бы добавили зависимость:
Или, если вы используете Gradle:
Давайте обновим наш предыдущий пример с помощью библиотеки ModelMapper:
Сопоставление с помощью структуры карты
Он использует обработку аннотаций для создания реализаций классов картографов во время компиляции и значительно сокращает объем шаблонного кода, который регулярно писался бы вручную.
Если вы используете Maven, установите MapStruct, добавив зависимость:
Эта зависимость будет импортировать аннотации основной структуры карты. Поскольку MapStruct работает во время компиляции и прикреплен к таким конструкторам, как Maven и Gradle, нам также придется добавить плагин в :
У нас уже есть наши Пользователь и Местоположение классы, так что давайте сделаем картограф для них:
Когда вы создадите проект, MapStruct подберет этот @Mapper и сгенерирует Класс отображения местоположения пользователя с полностью функционирующей реализацией.
Вывод
В этой статье мы рассмотрели Шаблон проектирования объекта передачи данных с его плюсами и минусами. Этот шаблон действительно предназначен только для удаленных вызовов, потому что преобразование из и в DTO может быть дорогостоящим.
Кроме того, мы создали демонстрационное приложение Spring Boot и изучили два популярных картографа, которые можно использовать для упрощения процесса сопоставления между моделями и DTO.