Exception Architecture
Status: Complete | Last Updated: 2025-11-20 | Version: 2.0.0
Navigation
Back to: Architecture Overview
Overview
The Smart Supply Pro exception handling architecture provides a centralized, layered approach to error management across the entire backend application. By separating framework-level exceptions from domain-specific business exceptions, we ensure consistent error responses, maintainable code, and secure error handling that prevents information disclosure.
This documentation hub covers: - Exception types and their mappings - Handler ordering and execution flow - Error response structure and correlation tracking - Security considerations and best practices - Implementation guidelines for developers
Quick Navigation
For Backend Developers
- Global Exception Handler β Framework-level exception handling, handler ordering, catch-all strategies
- Exception-to-HTTP Mapping β Complete status code mapping table and logic
- Domain Exceptions β Custom business logic exceptions (InvalidRequestException, DuplicateResourceException)
- Guidelines & Best Practices β When to throw, anti-patterns, error recovery
For Frontend/API Integration
- Error Response Structure β JSON format, fields, correlation IDs
- Exception-to-HTTP Mapping β Expected status codes and error tokens
- Validation Exceptions β Field-level error handling
For DevOps/Security
- Security Exceptions β Authentication/authorization errors, generic messaging
- Global Exception Handler β Sensitive data sanitization, error exposure prevention
Exception Type Classifications
1. Framework-Level Exceptions (GlobalExceptionHandler)
These are exceptions thrown by Spring Framework, Spring MVC, and other libraries. They are automatically caught and mapped to HTTP status codes.
Common Framework Exceptions: - MethodArgumentNotValidException β Validation failure on @Valid objects β 400 - ConstraintViolationException β JSR-380 constraint violation β 400 - HttpMessageNotReadableException β Malformed request body (e.g., invalid JSON) β 400 - MissingServletRequestParameterException β Missing required query/form parameter β 400 - MethodArgumentTypeMismatchException β Parameter type conversion failure β 400 - AuthenticationException β Authentication failure (credentials invalid) β 401 - AccessDeniedException β Authorization failure (insufficient permissions) β 403 - NoSuchElementException β Resource not found in collection β 404 - IllegalArgumentException β Invalid argument (business lookup failure) β 404 - DataIntegrityViolationException β Database constraint violation β 409 - ObjectOptimisticLockingFailureException β Concurrent update conflict β 409 - ResponseStatusException β Explicit HTTP status exception β Preserved status - Exception (catch-all) β Unhandled exceptions β 500 (no stack trace)
Characteristics: - β Handled automatically by GlobalExceptionHandler - β No code changes needed to propagate to error response - β Always return consistent error response format - β Sensitive information is sanitized (e.g., SQL details hidden)
2. Domain-Level Exceptions (BusinessExceptionHandler)
These are custom exceptions representing business rule violations. They are thrown explicitly from service layer code to handle domain-specific scenarios.
Current Domain Exceptions: - InvalidRequestException β Business validation failures, field-level errors β 400 - DuplicateResourceException β Uniqueness constraint violations β 409 - IllegalStateException β Business state rule violations β 409
Characteristics: - β Thrown explicitly from service methods - β Carry context information (field names, resource types, conflict details) - β Prioritized before framework exceptions (via @Order) - β Can include structured error details for client handling
3. Validation Exceptions
A specialized category of exceptions for handling request validation errors with field-level granularity.
Types: - InvalidRequestException with field errors β Specific field validation failures - MethodArgumentNotValidException β Springβs bean validation (@Valid) - ConstraintViolationException β JSR-380 constraints on method parameters
Fields Captured: - Field names (e.g., βemailβ, βquantityβ) - Validation codes (e.g., βNOT_EMPTYβ, βPOSITIVEβ) - Field-specific error messages (e.g., βEmail format is invalidβ)
4. Security Exceptions
Exceptions related to authentication and authorization with special handling for sensitive information.
Types: - AuthenticationException β Credentials invalid, user not found, token expired β 401 - AccessDeniedException β User lacks required permissions β 403 - InvalidRequestException with CRITICAL severity β Security validation failures
Security Behavior: - β οΈ Generic error messages (prevents user enumeration attacks) - β οΈ No details about why authentication/authorization failed - β οΈ Details logged server-side for debugging (never exposed to client) - β οΈ Correlation IDs enable server-side investigation
Exception Handling Flow
InvalidRequestException
DuplicateResourceException
IllegalStateException| E["BusinessExceptionHandler
@Order: HIGHEST_PRECEDENCE"] D -->|No
Framework Exception| F["GlobalExceptionHandler
@Order: HIGHEST_PRECEDENCE + 1"] E --> G["Map to HTTP Status
Extract Business Context"] F --> H["Map to HTTP Status
Sanitize Sensitive Data"] G --> I["Build ErrorResponse
- error token
- message
- timestamp
- correlationId"] H --> I I --> J["Return ResponseEntity
with JSON"] J --> K["HTTP Response to Client"] C --> K
Execution Order
- Request arrives at controller method
- Exception thrown during request processing
- BusinessExceptionHandler intercepts first
(@Order:
HIGHEST_PRECEDENCE)
- If domain exception β handle it, return error response
- If not domain exception β pass to next handler
- GlobalExceptionHandler intercepts second
(@Order:
HIGHEST_PRECEDENCE + 1)
- If framework/known exception β handle it, return error response
- If unhandled exception β catch-all handler (500, no stack trace)
- ErrorResponse built with timestamp and correlation ID
- ResponseEntity returned with proper HTTP status and JSON
HTTP Status Code Reference
| Status | Meaning | Common Causes | Handler |
|---|---|---|---|
| 400 | Bad Request | Invalid input, validation failure, malformed JSON | Global/Business |
| 401 | Unauthorized | Authentication failed, expired token | Global |
| 403 | Forbidden | Insufficient permissions, access denied | Global |
| 404 | Not Found | Resource doesnβt exist | Global |
| 409 | Conflict | Duplicate resource, concurrent update, state violation | Global/Business |
| 500 | Internal Server Error | Unhandled exception, unexpected error | Global |
Error Response Structure
All error responses follow this standard JSON structure:
{
"error": "bad_request",
"message": "Validation failed: email is required",
"timestamp": "2025-11-20T14:30:45.123Z",
"correlationId": "SSP-1700551445123-4891"
}Fields: - error β Machine-readable error token (lowercase HTTP status name) - message β Human-readable description of the error - timestamp β ISO-8601 UTC timestamp of error occurrence - correlationId β Unique tracking ID linking client request to server logs
See Error Response Structure for detailed field documentation.
Handler Architecture Diagram
File Organization
This exception architecture documentation is organized as follows:
Core Implementation
- Global Exception Handler β @RestControllerAdvice, handler methods, exception mapping
- Domain Exceptions β Custom exceptions, factory methods, context information
- Error Response Structure β DTO structure, builder pattern, correlation IDs
Reference & Mapping
- Exception-to-HTTP Mapping β Complete mapping table, logic, decision tree
- Validation Exceptions β Validation failure handling, field-level errors
- Security Exceptions β Auth/authz, generic messaging, security best practices
Developer Guidance
- Guidelines & Best Practices β When to throw, anti-patterns, error recovery strategies
Key Design Principles
1. Separation of Concerns
- Framework exceptions handled separately from domain exceptions
- Each handler focuses on its domain (framework vs.Β business)
- Clear responsibility boundaries
2. Handler Ordering
- BusinessExceptionHandler runs first (HIGHEST_PRECEDENCE)
- GlobalExceptionHandler runs second (HIGHEST_PRECEDENCE + 1)
- Ensures domain exceptions take priority over framework exceptions
3. Consistent Error Format
- All errors return same JSON structure (error, message, timestamp, correlationId)
- Predictable for client code to handle
- Correlation IDs enable request tracking across logs
4. Security by Default
- Generic messages for auth/authz (prevents user enumeration)
- No stack traces in 500 responses (prevents information disclosure)
- SQL errors sanitized (hides database schema)
- Sensitive data never exposed in error messages
5. Debuggability
- Correlation IDs tie client requests to server logs
- Timestamps help with log correlation
- Server-side logging captures full exception details (not exposed to client)
6. Extensibility
- New domain exceptions can be added to BusinessExceptionHandler
- New framework exception types caught by GlobalExceptionHandler
- Custom factory methods in domain exceptions simplify common scenarios
Integration Points
With Controllers
@RestController
@RequestMapping("/api/suppliers")
public class SupplierController {
@PostMapping
public ResponseEntity<SupplierDTO> createSupplier(@Valid @RequestBody CreateSupplierRequest req) {
// Validation errors thrown by @Valid β GlobalExceptionHandler
// BusinessExceptionHandler catches domain exceptions
return supplierService.createSupplier(req);
}
}With Service Layer
@Service
public class SupplierService {
public SupplierDTO createSupplier(CreateSupplierRequest req) {
// Check for duplicates β throw DuplicateResourceException
if (supplierExists(req.getName())) {
throw DuplicateResourceException.supplierName(req.getName());
}
// Business rule violation β throw IllegalStateException
if (!isValidSupplierType(req.getType())) {
throw new IllegalStateException("Supplier type is not supported");
}
}
}With Frontend
// Frontend receives error response with correlation ID
try {
await api.post('/suppliers', supplierData);
} catch (error) {
const correlationId = error.response.data.correlationId;
console.error(`Error ${error.response.status}: ${error.response.data.error}`);
console.error(`Correlation ID: ${correlationId}`);
console.error(`Message: ${error.response.data.message}`);
// User reports issue with correlation ID for debugging
}Related Documentation
- Global Exception Handler β Implementation details of framework exception handling
- Domain Exceptions β Custom exception classes and usage patterns
- Error Response Structure β JSON response format and fields
- Exception-to-HTTP Mapping β Complete status code mapping reference
- Guidelines & Best Practices β Developer best practices for exception handling
- Validation Exceptions β Handling request validation errors
- Security Exceptions β Authentication and authorization error handling
- Integration Architecture β Frontend-backend error contract
- Architecture Overview β Complete backend architecture documentation
Version History
| Version | Date | Changes |
|---|---|---|
| 2.0.0 | 2025-11-20 | Complete exception architecture documentation (8 files, 7000+ lines) |
| 1.0.0 | 2025-10-15 | Initial exception handling implementation |
Quick Start: Common Scenarios
Scenario 1: Validate Required Field
// Service
if (request.getEmail() == null || request.getEmail().isBlank()) {
throw InvalidRequestException.requiredField("email");
}
// Result: 400 with error token "bad_request"Scenario 2: Enforce Uniqueness
// Service
if (supplierRepository.existsByName(name)) {
throw DuplicateResourceException.supplierName(name);
}
// Result: 409 with error token "conflict" and resource contextScenario 3: Business Rule Violation
// Service
if (supplier.getStatus() == DELETED) {
throw new IllegalStateException("Cannot modify deleted supplier");
}
// Result: 409 with error token "conflict"Scenario 4: Handle Frontend Error
// Frontend
const response = await api.post('/suppliers', data);
if (response.status === 409 && response.data.error === 'conflict') {
const context = response.data; // { resourceType, conflictField, duplicateValue }
showConflictDialog(context);
}Performance & Observability
Correlation ID Benefits
- π Link client requests to server logs
- π Track error patterns across requests
- π Facilitate debugging user-reported issues
- π Analyze error distribution by type/status
Timestamp Tracking
- β±οΈ Order errors chronologically in logs
- π Correlate with other system events
- π Calculate error response times
Logging Best Practices
- β Log exception details server-side (full stack trace)
- β Include correlation ID in log entries
- β Never expose stack traces to clients
- β Never expose database schema details
Troubleshooting
Q: Iβm not seeing my custom exception handler called? - A: Ensure your custom exception extends RuntimeException and your handler is in a @RestControllerAdvice class
Q: How do I add context information to errors? - A: Use factory methods or constructors in custom exceptions to capture context (e.g., DuplicateResourceException)
Q: Why am I getting a generic 500 error instead of meaningful information? - A: Check logs for correlation ID; use it to find detailed server-side exception logs
Q: How do I prevent sensitive data in error messages? - A: Use the sanitize() method in GlobalExceptionHandler or generic exception messages
Next Steps
- Review Global Exception Handler for implementation details
- Study Exception-to-HTTP Mapping for complete status code reference
- Understand Domain Exceptions for custom exception patterns
- Follow Guidelines & Best Practices when implementing new exception handling