javaintermediate

Custom Exception Hierarchy

Design a clean exception hierarchy with custom exceptions, error codes, and exception mapping.

java
// Base application exception
public abstract class AppException extends RuntimeException {
    private final String errorCode;
    private final int httpStatus;

    protected AppException(String errorCode, int httpStatus, String message) {
        super(message);
        this.errorCode = errorCode;
        this.httpStatus = httpStatus;
    }

    protected AppException(String errorCode, int httpStatus, String message, Throwable cause) {
        super(message, cause);
        this.errorCode = errorCode;
        this.httpStatus = httpStatus;
    }

    public String getErrorCode() { return errorCode; }
    public int getHttpStatus() { return httpStatus; }
}

// Specific exceptions
public class NotFoundException extends AppException {
    public NotFoundException(String resource, Object id) {
        super("NOT_FOUND", 404,
              String.format("%s not found with id: %s", resource, id));
    }
}

public class ValidationException extends AppException {
    private final Map<String, String> fieldErrors;

    public ValidationException(Map<String, String> fieldErrors) {
        super("VALIDATION_ERROR", 400, "Validation failed");
        this.fieldErrors = fieldErrors;
    }

    public Map<String, String> getFieldErrors() { return fieldErrors; }
}

public class ConflictException extends AppException {
    public ConflictException(String message) {
        super("CONFLICT", 409, message);
    }
}

public class RateLimitException extends AppException {
    public RateLimitException() {
        super("RATE_LIMITED", 429, "Too many requests");
    }
}

// Usage in service
class UserService {
    public User findById(Long id) {
        return repo.findById(id)
            .orElseThrow(() -> new NotFoundException("User", id));
    }

    public User create(CreateUserDTO dto) {
        if (repo.existsByEmail(dto.email())) {
            throw new ConflictException("Email already registered");
        }
        return repo.save(toEntity(dto));
    }
}

Use Cases

  • Consistent error handling across services
  • Mapping business errors to HTTP status codes
  • Structured exception hierarchies for large codebases

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.