Spring Boot
Теория: Шаблон проектирования DTO
Spring Boot умеет автоматически преобразовывать объекты в JSON, когда они возвращаются из методов контроллера. Для этого внутри используется библиотека Jackson:
Несмотря на удобство, на практике этот механизм используют редко по нескольким причинам:
- Безопасность: Обычно у пользователя есть свойства, которые не стоит показывать наружу — например, хэш пароля или количество денег на счету. Автоматическое преобразование не учитывает такие данные и возвращает все доступные свойства.
- Представления: В разных ситуациях нужно возвращать разные наборы свойств. Для веб-версии нужно что-то одно, а для мобильной — что-то другое. Кроме того, по разным причинам могут отличаться названия свойств.
- Схема данных: Со временем имена полей могут меняться — например, из-за изменений в базе данных. При этом API меняться не должен, потому что на него рассчитывают клиенты. Разделение помогает асинхронно менять названия либо в сущностях, либо в API.
- Связи: Если в сущностях появляются связи с другими сущностями, это может вести к исключениям и другим проблемам во время преобразования в JSON.
В Jackson встроена аннотация @JsonIgnore, которая в простых случаях помогает решить проблемы с безопасностью. Если пометить этой аннотацией какое-то поле сущности, оно будет проигнорировано при конвертации в JSON:
У этого механизма есть две проблемы:
- Это антипаттерн, который нарушает саму суть MVC. Модель узнает, как она используется в слое представления.
- Этот механизм не решает проблемы, описанные выше. В разных ситуациях мы работаем с разными наборами полей с точки зрения безопасности и представлений. Аннотация
@JsonIgnoreработает, только когда существует единственное представление — в реальных проектах такое встречается редко.
Для решения этих задач был придуман шаблон проектирования Data Transfer Object (DTO). По этому паттерну мы должны создавать свой класс с особыми набором полей под каждую конкретную ситуацию, которая требует своего набора полей. Затем необходимые данные из модели нужно копировать в DTO и возвращать наружу.
Для примера выше нам понадобится класс UserDTO:
DTO — это не часть Spring Boot, поэтому именование и расположение этих классов лежит полностью на программистах. Мы будем хранить эти классы в директории src/main/java/dto.
Когда класс написан, остается только внедрить его в контроллер:
Таким же образом мы поступим и во всех остальных ситуациях. Например, со списками:
Для удобства мы вынесли преобразование сущности в DTO в отдельный приватный метод. Это помогает немного снизить уровень дублирования, но не освобождает от ручного копирования свойств из одного объекта в другой. В следующих уроках мы познакомимся с библиотекой для автоматического копирования свойств.
В наших примерах для списка и вывода конкретной сущности мы использовали один класс UserDTO, но это не обязательно. Если набор полей будет разным, то на каждый набор понадобится свой собственный класс: UserDTO, CreateUserDTO, UserListDTO, AdminUserListDTO и так далее.



