Files
michaelschiemer/docs/ERROR-HANDLING-UNIFIED-ARCHITECTURE.md
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

54 KiB

Error Handling - Unified Architecture Design

Date: 2025-10-12 Status: Design Phase Previous: ERROR-HANDLING-AUDIT-REPORT.md Next: ERROR-HANDLING-MIGRATION-PLAN.md

Executive Summary

Dieses Dokument definiert die Unified Exception Architecture für das Framework. Das Design konsolidiert 4 separate Error-Module in ein einheitliches System unter src/Framework/Exception/ mit automatischer Integration aller Komponenten.

Design Goals:

  • Single Source of Truth für Error Handling
  • Automatic integration (Pattern Detection, Reporting, Circuit Breaker)
  • Backward compatibility mit bestehenden Code
  • Improved developer experience
  • Maintained functionality from all modules
  • Performance optimization (async processing, batching)

1. New Module Structure

Unified Directory Layout

src/Framework/Exception/
├── Core/                              # Core exception infrastructure
│   ├── FrameworkException.php         # Enhanced base exception
│   ├── ErrorCode.php                  # Enhanced with severity mapping
│   ├── ExceptionContext.php           # Unified context (Domain + Request + System)
│   ├── ErrorSeverity.php              # Moved from ErrorAggregation
│   └── ErrorLevel.php                 # Moved from ErrorHandling
│
├── Handler/                           # Global error handling
│   ├── ErrorHandler.php               # Integrated error handler
│   ├── ErrorLogger.php                # Structured logging
│   ├── ResponseFactory.php            # HTTP response creation
│   └── ContextBuilder.php             # ExceptionContext builder
│
├── Aggregation/                       # Pattern detection & alerting
│   ├── ErrorAggregator.php            # Auto-triggered pattern analysis
│   ├── ErrorEvent.php                 # Single error event VO
│   ├── ErrorPattern.php               # Pattern VO
│   ├── AlertManager.php               # Alert queuing
│   ├── PatternDetector.php            # Fingerprinting logic
│   └── Storage/
│       ├── ErrorStorageInterface.php
│       └── DatabaseErrorStorage.php   # Unified database storage
│
├── Reporting/                         # Error reporting & analytics
│   ├── ErrorReporter.php              # Auto-triggered reporting
│   ├── ErrorReport.php                # Report VO (extends ErrorEvent)
│   ├── AnalyticsEngine.php            # Velocity & anomaly detection
│   ├── ReportFilters/                 # Filter chain
│   └── ReportProcessors/              # Enrichment chain
│
├── Boundaries/                        # Resilience & circuit breaker
│   ├── ErrorBoundary.php              # Graceful degradation
│   ├── BoundaryConfig.php             # Configuration VO
│   ├── BoundaryResult.php             # Result pattern
│   ├── RetryStrategy.php              # Retry patterns
│   ├── CircuitBreaker/
│   │   ├── CircuitBreakerManager.php
│   │   └── CircuitState.php
│   └── Exceptions/
│       ├── BoundaryException.php      # Base boundary exception (extends FrameworkException)
│       ├── BoundaryTimeoutException.php
│       └── BoundaryFailedException.php
│
├── Http/                              # HTTP-specific exceptions
│   ├── HttpException.php              # Base HTTP exception
│   ├── NotFoundException.php
│   ├── UnauthorizedException.php
│   └── ...
│
├── Security/                          # Security exceptions
│   ├── SecurityException.php
│   ├── XssAttemptException.php
│   └── ...
│
├── Database/                          # Database exceptions
│   ├── DatabaseException.php
│   └── ...
│
├── Validation/                        # Validation exceptions
│   ├── ValidationException.php
│   └── ...
│
└── Migrations/                        # Database migrations
    ├── CreateErrorEventsTable.php
    └── CreateErrorPatternsTable.php

Removed Directories

❌ src/Framework/ErrorHandling/        # Merged into Exception/Handler/
❌ src/Framework/ErrorAggregation/     # Merged into Exception/Aggregation/
❌ src/Framework/ErrorBoundaries/      # Merged into Exception/Boundaries/
❌ src/Framework/ErrorReporting/       # Merged into Exception/Reporting/

2. Core Components Design

2.1 Enhanced FrameworkException

File: src/Framework/Exception/Core/FrameworkException.php

<?php

declare(strict_types=1);

namespace App\Framework\Exception\Core;

use App\Framework\Exception\Aggregation\ErrorAggregator;
use App\Framework\Exception\Reporting\ErrorReporter;

/**
 * Enhanced FrameworkException with automatic integration
 *
 * All framework exceptions MUST extend this class to ensure:
 * - Automatic pattern detection via ErrorAggregator
 * - Automatic error reporting via ErrorReporter
 * - Consistent error handling and context
 * - Circuit breaker integration
 */
class FrameworkException extends \RuntimeException
{
    protected ExceptionContext $context;
    protected ?ErrorCode $errorCode = null;
    protected ?int $retryAfter = null;

    // NEW: Automatic integration flags
    protected bool $skipAggregation = false;
    protected bool $skipReporting = false;
    protected bool $reported = false;

    public function __construct(
        string $message = '',
        int $code = 0,
        ?\Throwable $previous = null
    ) {
        parent::__construct($message, $code, $previous);
        $this->context = ExceptionContext::empty();
    }

    // ========================================
    // Factory Methods (existing + enhanced)
    // ========================================

    public static function simple(
        string $message,
        ?\Throwable $previous = null,
        int $httpStatus = 500
    ): static {
        $exception = new static($message, $httpStatus, $previous);
        return $exception;
    }

    public static function create(
        ErrorCode $errorCode,
        ?string $message = null
    ): static {
        $exception = new static($message ?? $errorCode->getDescription());
        $exception->errorCode = $errorCode;
        return $exception;
    }

    public static function forOperation(
        string $operation,
        ?string $component = null,
        ?string $message = null,
        ?ErrorCode $errorCode = null
    ): static {
        $exception = new static($message ?? "Error in {$operation}");
        $exception->context = ExceptionContext::forOperation($operation, $component);
        $exception->errorCode = $errorCode;
        return $exception;
    }

    public static function fromContext(
        string $message,
        ExceptionContext $context,
        ?ErrorCode $errorCode = null
    ): static {
        $exception = new static($message);
        $exception->context = $context;
        $exception->errorCode = $errorCode;
        return $exception;
    }

    // ========================================
    // Fluent API (existing)
    // ========================================

    public function withContext(ExceptionContext $context): self
    {
        $this->context = $context;
        return $this;
    }

    public function withOperation(string $operation, ?string $component = null): self
    {
        $this->context = $this->context->withOperation($operation, $component);
        return $this;
    }

    public function withData(array $data): self
    {
        $this->context = $this->context->withData($data);
        return $this;
    }

    public function withDebug(array $debug): self
    {
        $this->context = $this->context->withDebug($debug);
        return $this;
    }

    public function withMetadata(array $metadata): self
    {
        $this->context = $this->context->withMetadata($metadata);
        return $this;
    }

    public function withErrorCode(ErrorCode $errorCode): self
    {
        $this->errorCode = $errorCode;
        return $this;
    }

    public function withRetryAfter(int $seconds): self
    {
        $this->retryAfter = $seconds;
        return $this;
    }

    // NEW: Request/System context methods
    public function withRequestContext(
        string $requestId,
        string $method,
        string $uri,
        ?string $clientIp = null,
        ?string $userAgent = null
    ): self {
        $this->context = $this->context->withRequestContext(
            $requestId,
            $method,
            $uri,
            $clientIp,
            $userAgent
        );
        return $this;
    }

    public function withSystemContext(
        string $environment,
        string $hostname,
        float $memoryUsage,
        float $cpuUsage
    ): self {
        $this->context = $this->context->withSystemContext(
            $environment,
            $hostname,
            $memoryUsage,
            $cpuUsage
        );
        return $this;
    }

    // NEW: Integration control methods
    public function skipAggregation(): self
    {
        $this->skipAggregation = true;
        return $this;
    }

    public function skipReporting(): self
    {
        $this->skipReporting = true;
        return $this;
    }

    // ========================================
    // Getters
    // ========================================

    public function getContext(): ExceptionContext
    {
        return $this->context;
    }

    public function getErrorCode(): ?ErrorCode
    {
        return $this->errorCode;
    }

    public function getRetryAfter(): ?int
    {
        return $this->retryAfter;
    }

    // NEW: Get severity from ErrorCode
    public function getSeverity(): ErrorSeverity
    {
        return $this->errorCode?->getSeverity() ?? ErrorSeverity::ERROR;
    }

    // ========================================
    // Recovery Methods
    // ========================================

    public function isRecoverable(): bool
    {
        return $this->errorCode?->isRecoverable() ?? true;
    }

    public function getRecoveryHint(): ?string
    {
        return $this->errorCode?->getRecoveryHint();
    }

    // NEW: Check if already reported
    public function isReported(): bool
    {
        return $this->reported;
    }

    public function markAsReported(): void
    {
        $this->reported = true;
    }

    public function shouldSkipAggregation(): bool
    {
        return $this->skipAggregation;
    }

    public function shouldSkipReporting(): bool
    {
        return $this->skipReporting;
    }

    // ========================================
    // Category Helpers
    // ========================================

    public function isCategory(string $category): bool
    {
        if ($this->errorCode === null) {
            return false;
        }

        return $this->errorCode->getCategory() === $category;
    }

    public function isErrorCode(ErrorCode $errorCode): bool
    {
        return $this->errorCode === $errorCode;
    }

    // ========================================
    // Serialization
    // ========================================

    public function toArray(): array
    {
        return [
            'message' => $this->getMessage(),
            'code' => $this->getCode(),
            'error_code' => $this->errorCode?->value,
            'severity' => $this->getSeverity()->value,
            'context' => $this->context->toArray(),
            'file' => $this->getFile(),
            'line' => $this->getLine(),
            'recoverable' => $this->isRecoverable(),
            'retry_after' => $this->retryAfter,
            'recovery_hint' => $this->getRecoveryHint(),
        ];
    }
}

Key Enhancements:

  1. Automatic integration flags (skipAggregation, skipReporting, reported)
  2. Request/System context support via withRequestContext(), withSystemContext()
  3. getSeverity() from ErrorCode
  4. Integration control methods (skipAggregation(), skipReporting())
  5. Category helpers (isCategory(), isErrorCode())

2.2 Unified ExceptionContext

File: src/Framework/Exception/Core/ExceptionContext.php

<?php

declare(strict_types=1);

namespace App\Framework\Exception\Core;

/**
 * Unified ExceptionContext combining Domain + Request + System context
 *
 * Replaces both:
 * - ExceptionContext (old)
 * - ErrorHandlerContext (old)
 */
final readonly class ExceptionContext
{
    public function __construct(
        // Domain Context
        public ?string $operation = null,
        public ?string $component = null,
        public array $data = [],
        public array $debug = [],
        public array $metadata = [],

        // Request Context (NEW)
        public ?string $requestId = null,
        public ?string $requestMethod = null,
        public ?string $requestUri = null,
        public ?string $clientIp = null,
        public ?string $userAgent = null,
        public ?string $userId = null,

        // System Context (NEW)
        public ?string $environment = null,
        public ?string $hostname = null,
        public ?float $memoryUsage = null,
        public ?float $cpuUsage = null,
        public ?string $phpVersion = null,
    ) {
    }

    // ========================================
    // Factory Methods
    // ========================================

    public static function empty(): self
    {
        return new self();
    }

    public static function forOperation(string $operation, ?string $component = null): self
    {
        return new self(operation: $operation, component: $component);
    }

    public static function fromRequest(
        string $requestId,
        string $method,
        string $uri,
        ?string $clientIp = null,
        ?string $userAgent = null,
        ?string $userId = null
    ): self {
        return new self(
            requestId: $requestId,
            requestMethod: $method,
            requestUri: $uri,
            clientIp: $clientIp,
            userAgent: $userAgent,
            userId: $userId
        );
    }

    public static function fromSystem(): self
    {
        return new self(
            environment: $_ENV['APP_ENV'] ?? 'production',
            hostname: gethostname() ?: 'unknown',
            memoryUsage: memory_get_usage(true) / 1024 / 1024, // MB
            cpuUsage: sys_getloadavg()[0] ?? 0.0,
            phpVersion: PHP_VERSION
        );
    }

    // ========================================
    // Fluent API - Domain Context
    // ========================================

    public function withOperation(string $operation, ?string $component = null): self
    {
        return new self(
            operation: $operation,
            component: $component ?? $this->component,
            data: $this->data,
            debug: $this->debug,
            metadata: $this->metadata,
            requestId: $this->requestId,
            requestMethod: $this->requestMethod,
            requestUri: $this->requestUri,
            clientIp: $this->clientIp,
            userAgent: $this->userAgent,
            userId: $this->userId,
            environment: $this->environment,
            hostname: $this->hostname,
            memoryUsage: $this->memoryUsage,
            cpuUsage: $this->cpuUsage,
            phpVersion: $this->phpVersion
        );
    }

    public function withData(array $data): self
    {
        return new self(
            operation: $this->operation,
            component: $this->component,
            data: array_merge($this->data, $data),
            debug: $this->debug,
            metadata: $this->metadata,
            requestId: $this->requestId,
            requestMethod: $this->requestMethod,
            requestUri: $this->requestUri,
            clientIp: $this->clientIp,
            userAgent: $this->userAgent,
            userId: $this->userId,
            environment: $this->environment,
            hostname: $this->hostname,
            memoryUsage: $this->memoryUsage,
            cpuUsage: $this->cpuUsage,
            phpVersion: $this->phpVersion
        );
    }

    public function withDebug(array $debug): self
    {
        return new self(
            operation: $this->operation,
            component: $this->component,
            data: $this->data,
            debug: array_merge($this->debug, $debug),
            metadata: $this->metadata,
            requestId: $this->requestId,
            requestMethod: $this->requestMethod,
            requestUri: $this->requestUri,
            clientIp: $this->clientIp,
            userAgent: $this->userAgent,
            userId: $this->userId,
            environment: $this->environment,
            hostname: $this->hostname,
            memoryUsage: $this->memoryUsage,
            cpuUsage: $this->cpuUsage,
            phpVersion: $this->phpVersion
        );
    }

    public function withMetadata(array $metadata): self
    {
        return new self(
            operation: $this->operation,
            component: $this->component,
            data: $this->data,
            debug: $this->debug,
            metadata: array_merge($this->metadata, $metadata),
            requestId: $this->requestId,
            requestMethod: $this->requestMethod,
            requestUri: $this->requestUri,
            clientIp: $this->clientIp,
            userAgent: $this->userAgent,
            userId: $this->userId,
            environment: $this->environment,
            hostname: $this->hostname,
            memoryUsage: $this->memoryUsage,
            cpuUsage: $this->cpuUsage,
            phpVersion: $this->phpVersion
        );
    }

    // ========================================
    // Fluent API - Request Context
    // ========================================

    public function withRequestContext(
        string $requestId,
        string $method,
        string $uri,
        ?string $clientIp = null,
        ?string $userAgent = null,
        ?string $userId = null
    ): self {
        return new self(
            operation: $this->operation,
            component: $this->component,
            data: $this->data,
            debug: $this->debug,
            metadata: $this->metadata,
            requestId: $requestId,
            requestMethod: $method,
            requestUri: $uri,
            clientIp: $clientIp,
            userAgent: $userAgent,
            userId: $userId,
            environment: $this->environment,
            hostname: $this->hostname,
            memoryUsage: $this->memoryUsage,
            cpuUsage: $this->cpuUsage,
            phpVersion: $this->phpVersion
        );
    }

    public function withUserId(string $userId): self
    {
        return new self(
            operation: $this->operation,
            component: $this->component,
            data: $this->data,
            debug: $this->debug,
            metadata: $this->metadata,
            requestId: $this->requestId,
            requestMethod: $this->requestMethod,
            requestUri: $this->requestUri,
            clientIp: $this->clientIp,
            userAgent: $this->userAgent,
            userId: $userId,
            environment: $this->environment,
            hostname: $this->hostname,
            memoryUsage: $this->memoryUsage,
            cpuUsage: $this->cpuUsage,
            phpVersion: $this->phpVersion
        );
    }

    // ========================================
    // Fluent API - System Context
    // ========================================

    public function withSystemContext(
        string $environment,
        string $hostname,
        float $memoryUsage,
        float $cpuUsage
    ): self {
        return new self(
            operation: $this->operation,
            component: $this->component,
            data: $this->data,
            debug: $this->debug,
            metadata: $this->metadata,
            requestId: $this->requestId,
            requestMethod: $this->requestMethod,
            requestUri: $this->requestUri,
            clientIp: $this->clientIp,
            userAgent: $this->userAgent,
            userId: $this->userId,
            environment: $environment,
            hostname: $hostname,
            memoryUsage: $memoryUsage,
            cpuUsage: $cpuUsage,
            phpVersion: $this->phpVersion
        );
    }

    // ========================================
    // Getters (for backward compatibility)
    // ========================================

    public function getOperation(): ?string
    {
        return $this->operation;
    }

    public function getComponent(): ?string
    {
        return $this->component;
    }

    public function getData(): array
    {
        return $this->data;
    }

    public function getDebug(): array
    {
        return $this->debug;
    }

    public function getMetadata(): array
    {
        return $this->metadata;
    }

    // ========================================
    // Serialization
    // ========================================

    public function toArray(): array
    {
        return [
            // Domain context
            'operation' => $this->operation,
            'component' => $this->component,
            'data' => $this->sanitizeData($this->data),
            'debug' => $this->debug,
            'metadata' => $this->metadata,

            // Request context
            'request' => [
                'id' => $this->requestId,
                'method' => $this->requestMethod,
                'uri' => $this->requestUri,
                'client_ip' => $this->clientIp,
                'user_agent' => $this->userAgent,
                'user_id' => $this->userId,
            ],

            // System context
            'system' => [
                'environment' => $this->environment,
                'hostname' => $this->hostname,
                'memory_usage_mb' => $this->memoryUsage,
                'cpu_usage' => $this->cpuUsage,
                'php_version' => $this->phpVersion,
            ],
        ];
    }

    private function sanitizeData(array $data): array
    {
        $sensitiveKeys = ['password', 'token', 'api_key', 'secret', 'private_key'];

        array_walk_recursive($data, function (&$value, $key) use ($sensitiveKeys) {
            if (in_array(strtolower($key), $sensitiveKeys)) {
                $value = '[REDACTED]';
            }
        });

        return $data;
    }
}

Key Enhancements:

  1. Unified context (Domain + Request + System)
  2. Replaces both ExceptionContext and ErrorHandlerContext
  3. Factory methods for different context types
  4. Fluent API preserved and extended
  5. Automatic data sanitization

2.3 Enhanced ErrorCode with Severity Mapping

File: src/Framework/Exception/Core/ErrorCode.php

<?php

declare(strict_types=1);

namespace App\Framework\Exception\Core;

enum ErrorCode: string
{
    // ... (alle bestehenden 100+ ErrorCodes bleiben)

    // NEW: Severity mapping
    public function getSeverity(): ErrorSeverity
    {
        return match($this) {
            // CRITICAL Severity
            self::SYSTEM_RESOURCE_EXHAUSTED,
            self::DB_CONNECTION_FAILED,
            self::DB_POOL_EXHAUSTED,
            self::SEC_SQL_INJECTION_ATTEMPT,
            self::SEC_XSS_ATTEMPT,
            self::SEC_PATH_TRAVERSAL_ATTEMPT,
            self::PERF_MEMORY_LIMIT_EXCEEDED,
            self::PERF_CIRCUIT_BREAKER_OPEN,
            self::FS_DISK_FULL => ErrorSeverity::CRITICAL,

            // ERROR Severity
            self::SYSTEM_DEPENDENCY_MISSING,
            self::DB_QUERY_FAILED,
            self::DB_TRANSACTION_FAILED,
            self::DB_MIGRATION_FAILED,
            self::AUTH_CREDENTIALS_INVALID,
            self::AUTH_INSUFFICIENT_PRIVILEGES,
            self::CACHE_CONNECTION_FAILED,
            self::FS_FILE_NOT_FOUND,
            self::FS_PERMISSION_DENIED,
            self::API_SERVICE_UNAVAILABLE,
            self::QUEUE_CONNECTION_FAILED,
            self::QUEUE_JOB_FAILED,
            self::PERF_EXECUTION_TIMEOUT,
            self::EVENT_DISPATCH_FAILED,
            self::TPL_RENDERING_FAILED => ErrorSeverity::ERROR,

            // WARNING Severity
            self::SYSTEM_CONFIG_INVALID,
            self::DB_CONSTRAINT_VIOLATION,
            self::AUTH_TOKEN_EXPIRED,
            self::AUTH_SESSION_EXPIRED,
            self::HTTP_RATE_LIMIT_EXCEEDED,
            self::API_RATE_LIMIT_EXCEEDED,
            self::CACHE_KEY_NOT_FOUND,
            self::QUEUE_MAX_RETRIES_EXCEEDED,
            self::PERF_THRESHOLD_EXCEEDED,
            self::TPL_TEMPLATE_NOT_FOUND => ErrorSeverity::WARNING,

            // INFO Severity
            self::HTTP_NOT_FOUND,
            self::ENTITY_NOT_FOUND,
            self::VAL_REQUIRED_FIELD_MISSING,
            self::VAL_INVALID_FORMAT,
            self::VAL_OUT_OF_RANGE => ErrorSeverity::INFO,

            // DEBUG Severity
            self::DISC_CACHE_CORRUPTION,
            self::TPL_CACHE_FAILED => ErrorSeverity::DEBUG,

            // Default to ERROR for unmapped codes
            default => ErrorSeverity::ERROR,
        };
    }

    // Existing methods remain unchanged
    public function getCategory(): string { ... }
    public function getNumericCode(): int { ... }
    public function getDescription(): string { ... }
    public function getRecoveryHint(): string { ... }
    public function isRecoverable(): bool { ... }
    public function getRetryAfterSeconds(): ?int { ... }
}

Key Enhancements:

  1. getSeverity() method mapping all 100+ codes to severities
  2. Explicit severity for each category
  3. Fallback to ERROR for unmapped codes

2.4 Integrated ErrorHandler

File: src/Framework/Exception/Handler/ErrorHandler.php

<?php

declare(strict_types=1);

namespace App\Framework\Exception\Handler;

use App\Framework\Exception\Aggregation\ErrorAggregator;
use App\Framework\Exception\Core\FrameworkException;
use App\Framework\Exception\Reporting\ErrorReporter;
use App\Framework\Http\Response;
use Throwable;

/**
 * Integrated ErrorHandler with automatic aggregation and reporting
 */
final readonly class ErrorHandler
{
    public function __construct(
        private ErrorLogger $logger,
        private ResponseFactory $responseFactory,
        private ContextBuilder $contextBuilder,
        private ErrorAggregator $aggregator,      // NEW: Auto-integrated
        private ErrorReporter $reporter,          // NEW: Auto-integrated
        private bool $enableAggregation = true,
        private bool $enableReporting = true,
    ) {
    }

    /**
     * Register global error handlers
     */
    public function register(): void
    {
        set_exception_handler($this->handleException(...));
        set_error_handler($this->handleError(...));
        register_shutdown_function($this->handleShutdown(...));
    }

    /**
     * Handle exception with automatic integration
     */
    public function handleException(Throwable $exception): void
    {
        try {
            // 1. Build unified context
            $context = $this->contextBuilder->buildFromException($exception);

            // 2. Enrich FrameworkException with full context
            if ($exception instanceof FrameworkException) {
                $exception = $this->enrichException($exception, $context);
            }

            // 3. Log the exception
            $this->logger->logException($exception, $context);

            // 4. Auto-trigger aggregation (if enabled and not skipped)
            if ($this->enableAggregation && !$this->shouldSkipAggregation($exception)) {
                $this->aggregator->processException($exception, $context);
            }

            // 5. Auto-trigger reporting (if enabled and not skipped)
            if ($this->enableReporting && !$this->shouldSkipReporting($exception)) {
                $this->reporter->reportException($exception, $context);
            }

            // 6. Create HTTP response (only if in HTTP context)
            if ($this->isHttpContext()) {
                $response = $this->responseFactory->createFromException($exception, $context);
                $this->emitResponse($response);
            }

        } catch (Throwable $e) {
            // Fallback: Don't let error handling crash the application
            $this->handleErrorHandlerFailure($e, $exception);
        }
    }

    /**
     * Enrich FrameworkException with full context
     */
    private function enrichException(
        FrameworkException $exception,
        ExceptionContext $fullContext
    ): FrameworkException {
        // Only enrich if exception doesn't already have full context
        if ($fullContext->requestId && !$exception->getContext()->requestId) {
            return $exception->withContext($fullContext);
        }

        return $exception;
    }

    /**
     * Check if aggregation should be skipped
     */
    private function shouldSkipAggregation(Throwable $exception): bool
    {
        if ($exception instanceof FrameworkException) {
            return $exception->shouldSkipAggregation();
        }

        return false;
    }

    /**
     * Check if reporting should be skipped
     */
    private function shouldSkipReporting(Throwable $exception): bool
    {
        if ($exception instanceof FrameworkException) {
            return $exception->shouldSkipReporting() || $exception->isReported();
        }

        return false;
    }

    /**
     * Create HTTP response from exception
     */
    public function createHttpResponse(
        Throwable $exception,
        ?ExceptionContext $context = null
    ): Response {
        $context ??= $this->contextBuilder->buildFromException($exception);
        return $this->responseFactory->createFromException($exception, $context);
    }

    // ... (rest of existing ErrorHandler methods)
}

Key Enhancements:

  1. Automatic ErrorAggregator integration
  2. Automatic ErrorReporter integration
  3. Respects skipAggregation() and skipReporting() flags
  4. Prevents duplicate reporting via isReported() check
  5. Graceful fallback if integration fails

2.5 Auto-Triggered ErrorAggregator

File: src/Framework/Exception/Aggregation/ErrorAggregator.php

<?php

declare(strict_types=1);

namespace App\Framework\Exception\Aggregation;

use App\Framework\Exception\Core\ExceptionContext;
use App\Framework\Exception\Core\FrameworkException;
use Throwable;

/**
 * Auto-triggered ErrorAggregator (called by ErrorHandler)
 */
final readonly class ErrorAggregator
{
    // ... (existing constructor and properties)

    /**
     * NEW: Process exception (called by ErrorHandler)
     */
    public function processException(
        Throwable $exception,
        ExceptionContext $context
    ): void {
        try {
            // Create ErrorEvent from exception + context
            $errorEvent = ErrorEvent::fromException($exception, $context);

            // Use existing processErrorEvent logic
            $this->processErrorEvent($errorEvent);

        } catch (\Throwable $e) {
            // Don't let aggregation break the application
            $this->logError("Failed to process exception: " . $e->getMessage(), [
                'exception' => $e,
                'original_exception' => $exception->getMessage(),
            ]);
        }
    }

    /**
     * Existing: Process ErrorEvent
     */
    public function processErrorEvent(ErrorEvent $event): void
    {
        // Existing logic remains unchanged
        $this->storage->storeEvent($event);
        $pattern = $this->updateErrorPattern($event);

        if ($pattern->shouldAlert()) {
            $this->queueAlert($pattern, $event);
        }

        $this->logError("Error processed", [
            'event_id' => $event->id->toString(),
            'fingerprint' => $event->getFingerprint(),
            'pattern_id' => $pattern->id->toString(),
        ]);
    }

    // ... (rest of existing methods remain unchanged)
}

Key Enhancements:

  1. New processException() method for ErrorHandler integration
  2. Creates ErrorEvent from Throwable + ExceptionContext
  3. Delegates to existing processErrorEvent() logic
  4. Graceful error handling

2.6 Enhanced ErrorEvent

File: src/Framework/Exception/Aggregation/ErrorEvent.php

<?php

declare(strict_types=1);

namespace App\Framework\Exception\Aggregation;

use App\Framework\Exception\Core\ExceptionContext;
use App\Framework\Exception\Core\FrameworkException;
use Throwable;

/**
 * Enhanced ErrorEvent with Throwable support
 */
final readonly class ErrorEvent
{
    // ... (existing properties remain)

    /**
     * NEW: Create from Throwable + ExceptionContext
     */
    public static function fromException(
        Throwable $exception,
        ExceptionContext $context
    ): self {
        // Extract ErrorCode (if FrameworkException)
        $errorCode = $exception instanceof FrameworkException
            ? $exception->getErrorCode() ?? self::inferErrorCode($exception)
            : self::inferErrorCode($exception);

        // Extract severity
        $severity = $exception instanceof FrameworkException
            ? $exception->getSeverity()
            : self::inferSeverity($exception);

        return new self(
            id: Ulid::generate(),
            service: self::extractServiceName($context),
            component: $context->component ?? 'unknown',
            operation: $context->operation ?? 'unknown',
            errorCode: $errorCode,
            errorMessage: $exception->getMessage(),
            severity: $severity,
            occurredAt: new \DateTimeImmutable(),
            context: $context->data,
            metadata: $context->metadata,
            requestId: $context->requestId,
            userId: $context->userId,
            clientIp: $context->clientIp,
            isSecurityEvent: self::isSecurityException($exception),
            stackTrace: $exception->getTraceAsString(),
            userAgent: $context->userAgent,
        );
    }

    /**
     * Existing: Create from ErrorHandlerContext (backward compatibility)
     */
    public static function fromErrorHandlerContext(ErrorHandlerContext $context): self
    {
        // Existing logic remains for backward compatibility during migration
        // Will be deprecated after migration complete
    }

    /**
     * Infer ErrorCode from generic Throwable
     */
    private static function inferErrorCode(Throwable $exception): ErrorCode
    {
        return match (true) {
            $exception instanceof \PDOException => ErrorCode::DB_QUERY_FAILED,
            $exception instanceof \RuntimeException => ErrorCode::SYSTEM_RESOURCE_EXHAUSTED,
            $exception instanceof \InvalidArgumentException => ErrorCode::VAL_INVALID_INPUT,
            default => ErrorCode::SYSTEM_RESOURCE_EXHAUSTED,
        };
    }

    /**
     * Infer severity from generic Throwable
     */
    private static function inferSeverity(Throwable $exception): ErrorSeverity
    {
        return match (true) {
            $exception instanceof \Error => ErrorSeverity::CRITICAL,
            $exception instanceof \PDOException => ErrorSeverity::ERROR,
            $exception instanceof \RuntimeException => ErrorSeverity::ERROR,
            default => ErrorSeverity::WARNING,
        };
    }

    /**
     * Check if exception is security-related
     */
    private static function isSecurityException(Throwable $exception): bool
    {
        if ($exception instanceof FrameworkException) {
            return $exception->isCategory('SEC');
        }

        return false;
    }

    // ... (rest of existing methods remain unchanged)
}

Key Enhancements:

  1. New fromException() factory method
  2. Automatic ErrorCode inference for non-FrameworkException
  3. Automatic severity inference
  4. Security exception detection
  5. Backward compatibility with fromErrorHandlerContext()

3. Integration Architecture

3.1 Automatic Integration Flow

┌─────────────────────────────────────────────────────────────────┐
│ 1. Exception Thrown                                             │
│    throw FrameworkException::create(ErrorCode::DB_QUERY_FAILED) │
└──────────────────────┬──────────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────────────┐
│ 2. ErrorHandler::handleException()                              │
│    - Build ExceptionContext (Domain + Request + System)         │
│    - Enrich FrameworkException with full context                │
│    - Log exception via ErrorLogger                              │
└──────────────────────┬──────────────────────────────────────────┘
                       │
        ┌──────────────┼──────────────┐
        │              │              │
        ▼              ▼              ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 3a. Aggregate│ │ 3b. Report   │ │ 3c. Respond  │
│ (async)      │ │ (async)      │ │ (sync)       │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
       │                │                │
       ▼                ▼                ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ ErrorEvent   │ │ ErrorReport  │ │ HTTP Response│
│ ErrorPattern │ │ Analytics    │ │ JSON/HTML    │
│ Alert Queue  │ │ Storage      │ │              │
└──────────────┘ └──────────────┘ └──────────────┘

3.2 ErrorBoundaries Integration

File: src/Framework/Exception/Boundaries/Exceptions/BoundaryException.php

<?php

declare(strict_types=1);

namespace App\Framework\Exception\Boundaries\Exceptions;

use App\Framework\Exception\Core\ErrorCode;
use App\Framework\Exception\Core\FrameworkException;

/**
 * Base exception for ErrorBoundaries (extends FrameworkException)
 */
class BoundaryException extends FrameworkException
{
    public function __construct(
        string $message,
        string $boundaryName,
        ?\Throwable $previous = null
    ) {
        parent::__construct($message, 0, $previous);

        $this->withOperation('boundary.execute', 'ErrorBoundary')
            ->withData(['boundary_name' => $boundaryName])
            ->withErrorCode(ErrorCode::PERF_CIRCUIT_BREAKER_OPEN);
    }
}

File: src/Framework/Exception/Boundaries/Exceptions/BoundaryTimeoutException.php

<?php

declare(strict_types=1);

namespace App\Framework\Exception\Boundaries\Exceptions;

use App\Framework\Exception\Core\ErrorCode;

final class BoundaryTimeoutException extends BoundaryException
{
    public function __construct(
        string $message,
        string $boundaryName,
        float $executionTimeSeconds,
        ?\Throwable $previous = null
    ) {
        parent::__construct($message, $boundaryName, $previous);

        $this->withData([
            'execution_time_seconds' => $executionTimeSeconds,
            'timeout_type' => 'boundary_timeout',
        ])->withErrorCode(ErrorCode::PERF_EXECUTION_TIMEOUT);
    }
}

Key Changes:

  1. BoundaryException extends FrameworkException
  2. Inherits ErrorCode, ExceptionContext, retry logic
  3. Automatically integrated with ErrorHandler
  4. Benefits from pattern detection and reporting

3.3 Database Schema Consolidation

Migration: src/Framework/Exception/Migrations/CreateErrorEventsTable.php

<?php

use App\Framework\Database\Schema\Blueprint;
use App\Framework\Database\Schema\Schema;

return new class {
    public function up(Schema $schema): void
    {
        $schema->create('error_events', function (Blueprint $table) {
            // Primary key
            $table->string('id', 26)->primary(); // ULID

            // Domain context
            $table->string('service', 50);
            $table->string('component', 100);
            $table->string('operation', 100);
            $table->string('error_code', 20);
            $table->text('error_message');
            $table->string('severity', 20); // CRITICAL, ERROR, WARNING, INFO, DEBUG

            // Timing
            $table->timestamp('occurred_at');

            // Context data (JSON)
            $table->json('context')->nullable();
            $table->json('metadata')->nullable();

            // Request context
            $table->string('request_id', 50)->nullable();
            $table->string('user_id', 50)->nullable();
            $table->string('client_ip', 45)->nullable(); // IPv6 support
            $table->text('user_agent')->nullable();

            // Security
            $table->boolean('is_security_event')->default(false);

            // Debugging
            $table->text('stack_trace')->nullable();

            // NEW: Reporting fields (merged from error_reports)
            $table->string('report_level', 20)->nullable(); // error, warning, info, debug
            $table->json('environment')->nullable(); // System context
            $table->boolean('is_reported')->default(false);
            $table->timestamp('reported_at')->nullable();

            // Indexes for performance
            $table->index(['service', 'occurred_at']);
            $table->index(['error_code', 'occurred_at']);
            $table->index(['severity', 'occurred_at']);
            $table->index('request_id');
            $table->index('user_id');
            $table->index(['is_security_event', 'occurred_at']);
            $table->index('occurred_at'); // For cleanup queries
        });
    }

    public function down(Schema $schema): void
    {
        $schema->drop('error_events');
    }
};

Migration: src/Framework/Exception/Migrations/CreateErrorPatternsTable.php

<?php

use App\Framework\Database\Schema\Blueprint;
use App\Framework\Database\Schema\Schema;

return new class {
    public function up(Schema $schema): void
    {
        $schema->create('error_patterns', function (Blueprint $table) {
            // Primary key
            $table->string('id', 26)->primary(); // ULID

            // Pattern identification
            $table->string('fingerprint', 64)->unique(); // SHA-256 hash

            // Pattern metadata
            $table->string('service', 50);
            $table->string('component', 100);
            $table->string('operation', 100);
            $table->string('error_code', 20);
            $table->text('normalized_message');
            $table->string('severity', 20);

            // Occurrence tracking
            $table->integer('occurrence_count')->default(1);
            $table->timestamp('first_occurrence');
            $table->timestamp('last_occurrence');

            // Impact tracking (JSON arrays)
            $table->json('affected_users')->nullable(); // Array of user IDs
            $table->json('affected_ips')->nullable(); // Array of IP addresses

            // Pattern lifecycle
            $table->boolean('is_active')->default(true);
            $table->boolean('is_acknowledged')->default(false);
            $table->string('acknowledged_by', 100)->nullable();
            $table->timestamp('acknowledged_at')->nullable();
            $table->text('resolution')->nullable();

            // Additional metadata
            $table->json('metadata')->nullable();

            // Timestamps
            $table->timestamp('created_at');
            $table->timestamp('updated_at');

            // Indexes for performance
            $table->index(['service', 'is_active', 'last_occurrence']);
            $table->index(['error_code', 'is_active']);
            $table->index(['severity', 'is_active', 'occurrence_count']);
            $table->index(['is_active', 'is_acknowledged']);
            $table->index('last_occurrence'); // For cleanup queries
        });
    }

    public function down(Schema $schema): void
    {
        $schema->drop('error_patterns');
    }
};

Key Changes:

  1. Merged error_reports fields into error_events
  2. Single source of truth für error storage
  3. Optimized indexes for common queries
  4. Retained all functionality from both tables

3.4 Backward Compatibility Strategy

Compatibility Layer: src/Framework/Exception/Compatibility/ErrorHandlerContextAdapter.php

<?php

declare(strict_types=1);

namespace App\Framework\Exception\Compatibility;

use App\Framework\Exception\Core\ExceptionContext;

/**
 * Adapter for old ErrorHandlerContext (backward compatibility)
 *
 * @deprecated Will be removed in next major version
 */
final readonly class ErrorHandlerContextAdapter
{
    /**
     * Convert old ErrorHandlerContext to new ExceptionContext
     */
    public static function toExceptionContext($oldContext): ExceptionContext
    {
        // Map old context structure to new unified context
        return ExceptionContext::empty()
            ->withOperation(
                $oldContext->exception->operation ?? 'unknown',
                $oldContext->exception->component ?? 'unknown'
            )
            ->withData($oldContext->exception->data ?? [])
            ->withDebug($oldContext->exception->debug ?? [])
            ->withMetadata($oldContext->exception->metadata ?? [])
            ->withRequestContext(
                $oldContext->request->requestId ?? '',
                $oldContext->request->method ?? 'GET',
                $oldContext->request->requestUri ?? '/',
                $oldContext->request->clientIp ?? null,
                $oldContext->request->userAgent ?? null,
                $oldContext->request->userId ?? null
            )
            ->withSystemContext(
                $oldContext->system->environment ?? 'production',
                $oldContext->system->hostname ?? 'unknown',
                $oldContext->system->memoryUsageMb ?? 0.0,
                $oldContext->system->cpuUsage ?? 0.0
            );
    }
}

Deprecation Notice: src/Framework/ErrorHandling/ErrorHandlerContext.php

<?php

namespace App\Framework\ErrorHandling;

/**
 * @deprecated Use \App\Framework\Exception\Core\ExceptionContext instead
 * Will be removed in next major version
 */
final readonly class ErrorHandlerContext
{
    // Keep existing implementation for backward compatibility
    // Add deprecation notice in constructor

    public function __construct(...)
    {
        trigger_error(
            'ErrorHandlerContext is deprecated. Use ExceptionContext instead.',
            E_USER_DEPRECATED
        );

        // ... existing constructor logic
    }
}

4. Performance Optimizations

4.1 Async Processing

Queue Integration for ErrorAggregation and ErrorReporting:

// In ErrorHandler::handleException()
if ($this->enableAggregation && !$this->shouldSkipAggregation($exception)) {
    // Queue aggregation instead of synchronous processing
    $this->queue->push('error_aggregation', [
        'exception_class' => get_class($exception),
        'exception_data' => $exception->toArray(),
        'context' => $context->toArray(),
    ]);
}

if ($this->enableReporting && !$this->shouldSkipReporting($exception)) {
    // Queue reporting instead of synchronous processing
    $this->queue->push('error_reporting', [
        'exception_class' => get_class($exception),
        'exception_data' => $exception->toArray(),
        'context' => $context->toArray(),
    ]);
}

Background Jobs:

  • ProcessErrorAggregationJob - Processes queued aggregation
  • ProcessErrorReportingJob - Processes queued reports
  • CleanupExpiredErrorEventsJob - Cleanup old error events

4.2 Caching Strategy

Pattern Cache (existing, enhanced):

// Cache error patterns with fingerprint key
$cacheKey = 'error_pattern:' . $fingerprint;
$this->cache->remember($cacheKey, fn() => $pattern, Duration::fromHours(1));

ErrorCode Severity Cache (new):

// Cache ErrorCode → ErrorSeverity mapping
private static array $severityCache = [];

public function getSeverity(): ErrorSeverity
{
    if (isset(self::$severityCache[$this->value])) {
        return self::$severityCache[$this->value];
    }

    $severity = match($this) { ... };
    self::$severityCache[$this->value] = $severity;

    return $severity;
}

4.3 Database Optimizations

Batch Inserts for error_events:

// Batch insert instead of individual inserts
public function storeEventsBatch(array $events): void
{
    $data = array_map(fn($event) => $event->toArray(), $events);
    $this->db->table('error_events')->insert($data);
}

Index Strategy:

  • Composite indexes for common query patterns
  • Separate indexes for cleanup queries (by occurred_at)
  • JSON column indexes for metadata searches (PostgreSQL)

Partitioning (future optimization):

  • Partition error_events by occurred_at (monthly partitions)
  • Automatic partition management
  • Faster cleanup queries

5. Testing Strategy

5.1 Unit Tests

Test Coverage Requirements: ≥95% for all core components

Priority Test Suites:

  1. FrameworkExceptionTest.php

    • Factory methods
    • Fluent API
    • Context enrichment
    • Severity mapping
    • Serialization
  2. ExceptionContextTest.php

    • Factory methods
    • Fluent API for all context types
    • Data sanitization
    • Serialization
  3. ErrorCodeTest.php

    • Severity mapping for all 100+ codes
    • Category extraction
    • Recovery hints
    • Retry timing
  4. ErrorHandlerTest.php

    • Automatic aggregation
    • Automatic reporting
    • Skip flags
    • Backward compatibility
  5. ErrorAggregatorTest.php

    • Pattern detection
    • Fingerprinting
    • Alert triggering
    • Batch processing
  6. ErrorBoundaryIntegrationTest.php

    • BoundaryException extends FrameworkException
    • Automatic integration
    • Circuit breaker

5.2 Integration Tests

End-to-End Error Flow:

it('handles exception end-to-end with automatic integration', function () {
    // 1. Throw exception
    $exception = FrameworkException::create(
        ErrorCode::DB_QUERY_FAILED,
        'Database query failed'
    );

    // 2. Error handler processes
    $this->errorHandler->handleException($exception);

    // 3. Verify aggregation triggered
    expect($this->queue)->toHaveJob('error_aggregation');

    // 4. Process aggregation job
    $this->processQueue('error_aggregation');

    // 5. Verify pattern created
    $patterns = $this->errorAggregator->getActivePatterns();
    expect($patterns)->toHaveCount(1);
    expect($patterns[0]->errorCode)->toBe('DB002');

    // 6. Verify reporting triggered
    expect($this->queue)->toHaveJob('error_reporting');

    // 7. Process reporting job
    $this->processQueue('error_reporting');

    // 8. Verify report created
    $reports = $this->errorReporter->getRecentReports();
    expect($reports)->toHaveCount(1);
});

5.3 Performance Tests

Benchmarks:

it('creates exception in <1ms', function () {
    $start = microtime(true);

    $exception = FrameworkException::create(
        ErrorCode::DB_QUERY_FAILED,
        'Test exception'
    );

    $duration = (microtime(true) - $start) * 1000;
    expect($duration)->toBeLessThan(1.0);
});

it('processes 1000 errors in <5 seconds', function () {
    $start = microtime(true);

    for ($i = 0; $i < 1000; $i++) {
        $exception = FrameworkException::create(
            ErrorCode::DB_QUERY_FAILED,
            "Error {$i}"
        );
        $this->errorHandler->handleException($exception);
    }

    $duration = microtime(true) - $start;
    expect($duration)->toBeLessThan(5.0);
});

6. Migration Checklist

Phase 1: Core Infrastructure

  • Create new Exception/ directory structure
  • Implement enhanced FrameworkException
  • Implement unified ExceptionContext
  • Enhance ErrorCode with severity mapping
  • Create ErrorSeverity enum
  • Write unit tests for core components

Phase 2: Handler Integration

  • Implement integrated ErrorHandler
  • Create ContextBuilder
  • Create ResponseFactory
  • Implement async queue integration
  • Write integration tests

Phase 3: Aggregation Integration

  • Enhance ErrorAggregator with processException()
  • Enhance ErrorEvent with fromException()
  • Update ErrorPattern (no changes needed)
  • Migrate database schema
  • Write aggregation tests

Phase 4: Reporting Integration

  • Enhance ErrorReporter with reportException()
  • Merge ErrorReport with ErrorEvent
  • Update AnalyticsEngine
  • Write reporting tests

Phase 5: Boundaries Integration

  • Create BoundaryException extending FrameworkException
  • Update BoundaryTimeoutException
  • Update BoundaryFailedException
  • Update ErrorBoundary to use new exceptions
  • Write boundaries integration tests

Phase 6: Backward Compatibility

  • Create ErrorHandlerContextAdapter
  • Add deprecation notices
  • Update documentation
  • Create migration guide

Phase 7: Cleanup

  • Remove old ErrorHandling/ directory
  • Remove old ErrorAggregation/ directory
  • Remove old ErrorBoundaries/ directory
  • Remove old ErrorReporting/ directory
  • Update all imports across codebase

7. Success Metrics

Before

  • 4 separate error modules
  • Manual integration required
  • 3 database tables
  • Fragmented context
  • Inconsistent exception types
  • ~2.000 lines across modules

After

  • 1 unified Exception module
  • Automatic integration
  • 2 database tables (consolidated)
  • Unified ExceptionContext
  • All exceptions extend FrameworkException
  • ~2.500 lines (enhanced functionality)
  • <1ms exception creation
  • <5ms pattern detection (async)
  • <10ms error reporting (async)

8. Next Steps

  1. Review & Approval - Review this design document
  2. Migration Plan - Create detailed step-by-step migration plan
  3. Prototyping - Build proof-of-concept for critical components
  4. Testing Strategy - Define comprehensive test suite
  5. Implementation - Execute migration in phases
  6. Documentation - Update all framework documentation
  7. Release - Plan major version release

Status: Design Phase Complete Next Document: ERROR-HANDLING-MIGRATION-PLAN.md Estimated Implementation: 3-5 days