Exception Design in Spring Layered Architecture
Fast recall
Quick revision
Core answer
@RestControllerAdvice. Separate safe client messages from internal diagnostics, and do not wrap every exception at every layer.Whether you can design an exception strategy across repository/service/controller layers with centralized handling
Scattering try-catch and HTTP status logic across controllers, or letting SQLException leak to the API
Show the @RestControllerAdvice central handler mapping exception types to status codes, and exception translation at boundaries
Interview-ready guide
Interview answer
I keep a small set of application exceptions named after meaningful failures, not one wrapper per layer. I translate only when crossing an abstraction boundary and preserve the original cause. For supported persistence APIs, I normally use Spring's existing DataAccessException translation instead of catching every SQLException myself.
A RestControllerAdvice maps known application exceptions to stable HTTP statuses and safe response bodies. Unexpected exceptions become a generic server response and are logged once internally with correlation context. I do not expose stack traces or raw database messages, and I reuse framework validation errors where they already express the problem.
Exception Design in Spring Layered Architecture
Direct Answer
In a layered Spring application, use a small set of meaningful domain or application exceptions, let Spring's data-access translation handle supported persistence technologies, translate only at real abstraction boundaries while preserving causes, and map failures centrally with @RestControllerAdvice. Separate safe client messages from internal diagnostics, and do not wrap every exception at every layer.
Code Example
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) { super(message); }
}
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ApiError> handleNotFound(ResourceNotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ApiError(404, e.getMessage()));
}
@ExceptionHandler(ValidationException.class)
public ResponseEntity<ApiError> handleValidation(ValidationException e) {
return ResponseEntity.badRequest().body(new ApiError(400, e.getMessage()));
}
}
One place owns HTTP status mapping; controllers and services throw plain domain exceptions.
Common Mistakes & Pitfalls
- Letting
SQLExceptionleak past the repository — the service and web layers end up coupled to JDBC. - Scattering try-catch and status codes across controllers — error mapping belongs in one
@RestControllerAdvice. - Using one generic exception for everything — distinct types like
ResourceNotFoundExceptionandValidationExceptionare what enable precise HTTP mapping. - Making domain exceptions HTTP-aware — the service shouldn't know about
404; that's the advice layer's job.
Check your understanding
Answer these without looking back at the article.
- What is @ControllerAdvice and @RestControllerAdvice?
- How do you return a structured error response body from an exception handler?
- What HTTP status code should a UserNotFoundException return?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.