Почему не стоит делать запросы к стороннему сервису в контроллере, а лучше в сервисе
Что проверяют
Понимаете ли вы ответственность слоёв в приложении на Spring и можете ли назвать последствия в проде, а не процитировать принцип единственной ответственности.
Как отвечать
Контроллер отвечает за протокол: разобрать запрос, проверить вход, отдать статус и тело. Всё остальное живёт в сервисе. Аргументы лучше давать практические.
Транзакции. @Transactional на контроллере означает открытую транзакцию на всё время внешнего вызова: строки заблокированы, соединение из пула занято, чужой таймаут превращается в очередь у вашей базы. Правило простое: не держать транзакцию вокруг сетевого вызова.
Переиспользование. Ту же логику завтра позовут из планировщика, слушателя очереди и другого контроллера. Из метода, который принимает HttpServletRequest, её не позвать.
Устойчивость. Таймауты, повторы, размыкатель цепи, перевод чужих ошибок в свои исключения живут в сервисе, а @RestControllerAdvice превращает исключение в код ответа. Иначе формат ошибки чужого API протекает наружу к вашим клиентам.
Тесты. Сервис проверяется с подменённым клиентом, контроллер срезом с моком сервиса. Если вызов сидит в контроллере, любой тест требует поднятого стаба.
Хорошо добавить про модель: DTO стороннего API не должны становиться вашими ответами, сервис переводит их в доменные объекты.
Где спотыкаются
Отвечают одним словом про SOLID и чистую архитектуру. Интервьюер ждёт, что именно сломается: назовите транзакцию вокруг сетевого вызова или невозможность позвать логику из планировщика.
Говорят, что перенос в сервис ускоряет обработку запроса. Сам по себе не ускоряет, сетевой вызов занимает столько же. Быстрее становится, когда появляются таймаут, кэш или асинхронность, и это отдельные решения, а не следствие переноса.
Переносят вызов, но тащат за ним контроллерные вещи: ResponseEntity, коды статусов, объект запроса. Слой появился формально, зависимость от веб-уровня осталась, и переиспользовать сервис по-прежнему нельзя.