Карта / Spring MVC / REST / DTO и @Valid

DTO и @Valid

DTO (Data Transfer Object) — отдельный класс для передачи данных через API, не тот же класс, что хранится в базе (@Entity). Разделение нужно, чтобы внутренняя структура базы не “протекала” наружу в публичный API.

// Entity — то, что хранится в базе
@Entity
class User {
    @Id Long id;
    String name;
    String passwordHash;   // это точно не должно уходить наружу!
}

// DTO — то, что видит клиент
record UserDto(Long id, String name) {}   // без passwordHash

@Valid — автоматическая проверка входных данных по правилам, описанным аннотациями прямо на полях DTO:

record CreateUserRequest(
    @NotBlank String name,
    @Email String email,
    @Min(18) int age
) {}

@PostMapping
User createUser(@Valid @RequestBody CreateUserRequest request) {
    // если данные не прошли проверку — Spring сам вернёт 400 Bad Request,
    // метод даже не вызовется
}
Копнуть глубже

Зачем DTO, если можно отдавать @Entity напрямую:

  • Безопасность — не утекают чувствительные поля (пароли, внутренние флаги);
  • Гибкость API — можно менять структуру базы, не ломая контракт API, и наоборот;
  • Защита от over-posting — если бы клиент мог напрямую слать @Entity в @RequestBody, он мог бы случайно (или специально) передать поле типа isAdmin: true, которого по сути не должно быть в запросе создания пользователя.

Частые аннотации валидации: @NotNull, @NotBlank (строка не пустая), @Min/@Max, @Email, @Size(min, max), @Pattern(regexp). Если валидация не пройдена, по умолчанию Spring бросает MethodArgumentNotValidException — её удобно ловить через @ExceptionHandler (см. тему «ExceptionHandler») и вернуть понятное клиенту сообщение об ошибке вместо стандартного стектрейса.

🎤 Закрыл тему, если можешь объяснить:
• зачем нужен отдельный DTO вместо Entity напрямую;
• как `@Valid` автоматически проверяет входные данные (если дошёл до 2-го слоя).