Опишите структуру контроллера, сервиса и репозитория для создания пользователя
Что проверяют
Понимаете ли вы, зачем слои вообще нужны и что лежит в каждом. Вопрос выглядит простым, но по ответу видно, писали ли вы боевой код: где валидация, где граница транзакции, что уходит наружу в теле ответа.
Как отвечать
Разложите ответственность и не смешивайте слои.
Контроллер знает только про HTTP: принимает CreateUserRequest с @Valid, отдаёт 201 и заголовок Location, про базу не знает ничего.
@PostMapping
ResponseEntity<UserResponse> create(@Valid @RequestBody CreateUserRequest req) {
var created = userService.create(req);
return ResponseEntity.created(URI.create("/users/" + created.id())).body(created);
}Сервис держит бизнес-правила и транзакцию: проверяет, что почта свободна, хэширует пароль, собирает сущность, сохраняет, при необходимости публикует событие. Аннотация @Transactional висит именно здесь.
Репозиторий это тонкий доступ к данным: interface UserRepository extends JpaRepository<UserEntity, Long> плюс existsByEmail. Никакой логики внутри.
Отдельно проговорите три вещи, которые обычно и хотят услышать: отдельные DTO на вход и выход, единая обработка ошибок через @RestControllerAdvice, внедрение зависимостей конструктором, а не полем.
Где спотыкаются
Принимают и возвращают JPA-сущность прямо в контроллере. Наружу утекают хэш пароля и служебные поля, ленивые связи взрываются при сериализации, а переименование колонки ломает контракт API.
Второе: уникальность почты проверяют только запросом перед вставкой. Два параллельных запроса проходят проверку одновременно, и в базе оказываются два пользователя. Нужен уникальный индекс в схеме, а DataIntegrityViolationException переводится в понятный клиенту 409.
Третье: @Transactional вешают на контроллер или зовут аннотированный метод изнутри того же класса через this. Прокси Spring такой вызов не перехватывает, и транзакции фактически нет, хотя аннотация в коде есть.