Почему нельзя сразу возвращать сущности из сервиса, а нужно использовать DTO или вьюшки
Что проверяют
Сталкивались ли вы с тем, как сущность JPA ведёт себя за границей транзакции, или заучили список преимуществ. Ответ про «инкапсуляцию и безопасность» без единого слова про Hibernate звучит как пересказ статьи.
Как отвечать
Главный аргумент конкретный, а не архитектурный: сущность это не структура данных, а объект под управлением персистентного контекста, и наружу он не приспособлен.
- Ленивые связи. Сериализация сущности вне транзакции даёт
LazyInitializationException, а если держать сессию открытой на весь запрос, сериализатор пройдёт по графу связей и наделает лишних запросов. - Двусторонние связи
@OneToManyи@ManyToOneпри сериализации зацикливаются и переполняют стек. - Изменение поля управляемой сущности где-нибудь в контроллере уедет в базу при сбросе контекста, без всякого явного сохранения.
- Схема базы протекает в контракт: переименовали колонку, сломали клиентов. А поля вроде хеша пароля или внутреннего комментария уезжают наружу просто потому, что они есть в таблице.
DTO разрывает эту связь: отдельный тип с нужными полями, маппинг руками или через MapStruct. Для чтения ещё лучше проекции Spring Data или выборка через конструктор в JPQL, тогда из базы поднимаются только нужные колонки.
Сильный ответ заканчивается границей применимости: во внутренних вызовах между слоями и в небольшом сервисе с одним потребителем маппинг один в один это цена без выгоды.
Где спотыкаются
Знают только аргумент про лишние поля. На уточнение, что будет при отдаче сущности с ленивой коллекцией, ответа нет, а это и есть главная практическая причина.
Делают один универсальный DTO на чтение, создание и обновление. В нём приходится держать идентификатор, которого при создании ещё нет, и обязательные поля, которых при частичном изменении не присылают.
Навешивают на сущность аннотации сериализатора и считают задачу решённой. Лишние поля скрыты, но схема таблицы так и осталась контрактом API, и первая же миграция это покажет.