# 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 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 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 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 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 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 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 withOperation('boundary.execute', 'ErrorBoundary') ->withData(['boundary_name' => $boundaryName]) ->withErrorCode(ErrorCode::PERF_CIRCUIT_BREAKER_OPEN); } } ``` **File**: `src/Framework/Exception/Boundaries/Exceptions/BoundaryTimeoutException.php` ```php 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 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 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 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 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): ```php // Cache error patterns with fingerprint key $cacheKey = 'error_pattern:' . $fingerprint; $this->cache->remember($cacheKey, fn() => $pattern, Duration::fromHours(1)); ``` **ErrorCode Severity Cache** (new): ```php // 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: ```php // 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**: ```php 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**: ```php 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