refactor(domain): introduce domain exceptions boundary
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- Create domain/errors.py as canonical error hierarchy: DomainError, InvalidStateTransition, PermissionViolation, BusinessRuleViolation, EntityNotFoundError, DuplicateEntityError - InvalidOperationError now inherits from BusinessRuleViolation for semantic consistency - Convert domain/exceptions.py to backward-compatible re-export shim with legacy aliases (DomainException, InvalidTransitionError, AuthorizationError) - Update error_handler.py to import from domain/errors.py and map all new error types - Update main.py to register DomainError (new base) as the exception handler root
This commit is contained in:
@@ -1,41 +1,43 @@
|
||||
"""Domain exception → HTTP response mapping.
|
||||
"""Domain error → HTTP response mapping.
|
||||
|
||||
This module provides a single exception handler that converts
|
||||
domain-layer exceptions into structured JSON responses, keeping
|
||||
domain-layer errors into structured JSON responses, keeping
|
||||
the service layer free from FastAPI's ``HTTPException``.
|
||||
"""
|
||||
|
||||
from fastapi import Request
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from app.domain.exceptions import (
|
||||
AuthorizationError,
|
||||
DomainException,
|
||||
from app.domain.errors import (
|
||||
BusinessRuleViolation,
|
||||
DomainError,
|
||||
DuplicateEntityError,
|
||||
EntityNotFoundError,
|
||||
InvalidOperationError,
|
||||
InvalidTransitionError,
|
||||
InvalidStateTransition,
|
||||
PermissionViolation,
|
||||
)
|
||||
|
||||
EXCEPTION_STATUS_MAP: dict[type[DomainException], int] = {
|
||||
EXCEPTION_STATUS_MAP: dict[type[DomainError], int] = {
|
||||
EntityNotFoundError: 404,
|
||||
DuplicateEntityError: 409,
|
||||
InvalidTransitionError: 400,
|
||||
InvalidStateTransition: 400,
|
||||
InvalidOperationError: 400,
|
||||
AuthorizationError: 403,
|
||||
BusinessRuleViolation: 400,
|
||||
PermissionViolation: 403,
|
||||
}
|
||||
|
||||
|
||||
async def domain_exception_handler(
|
||||
request: Request,
|
||||
exc: DomainException,
|
||||
exc: DomainError,
|
||||
) -> JSONResponse:
|
||||
"""Convert a :class:`DomainException` into a JSON error response."""
|
||||
"""Convert a :class:`DomainError` into a JSON error response."""
|
||||
status_code = EXCEPTION_STATUS_MAP.get(type(exc), 400)
|
||||
|
||||
content: dict = {"detail": exc.message, "code": exc.code}
|
||||
|
||||
if isinstance(exc, InvalidTransitionError):
|
||||
if isinstance(exc, InvalidStateTransition):
|
||||
content["current_state"] = exc.current_state
|
||||
content["target_state"] = exc.target_state
|
||||
content["valid_transitions"] = exc.valid_transitions
|
||||
|
||||
Reference in New Issue
Block a user