# Error Boundaries Error Boundaries provide graceful degradation and prevent cascading failures in the application. They act as a safety net that catches errors and provides fallback functionality instead of letting the entire system fail. ## Overview The Error Boundary system implements multiple patterns for resilient error handling: - **Fallback Mechanisms** - Provide alternative functionality when operations fail - **Retry Strategies** - Automatically retry failed operations with configurable strategies - **Circuit Breaker Pattern** - Prevent repeated calls to failing services - **Bulk Operations** - Handle partial failures in batch processing - **Timeout Protection** - Prevent long-running operations from blocking the system ## Basic Usage ### Simple Error Boundary ```php use App\Framework\ErrorBoundaries\ErrorBoundary; use App\Framework\ErrorBoundaries\BoundaryConfig; $boundary = new ErrorBoundary('user_service', BoundaryConfig::externalService()); $result = $boundary->execute( operation: fn() => $userService->getUser($id), fallback: fn() => $this->getCachedUser($id) ); ``` ### Factory Pattern ```php use App\Framework\ErrorBoundaries\ErrorBoundaryFactory; $factory = $container->get(ErrorBoundaryFactory::class); // Create boundary for different contexts $dbBoundary = $factory->createForDatabase('user_queries'); $apiBoundary = $factory->createForExternalService('payment_api'); $uiBoundary = $factory->createForUI('user_dashboard'); ``` ## Configuration ### Predefined Configurations ```php // Critical operations - maximum resilience BoundaryConfig::critical() // External services - network-aware retries BoundaryConfig::externalService() // Database operations - transaction-safe retries BoundaryConfig::database() // UI components - fast failure for user experience BoundaryConfig::ui() // Background jobs - long retry cycles BoundaryConfig::backgroundJob() // Development - permissive for debugging BoundaryConfig::development() // Fail fast - no retries BoundaryConfig::failFast() ``` ### Custom Configuration ```php $config = new BoundaryConfig( maxRetries: 3, retryStrategy: RetryStrategy::EXPONENTIAL_JITTER, baseDelay: Duration::fromMilliseconds(100), maxDelay: Duration::fromSeconds(5), circuitBreakerEnabled: true, circuitBreakerThreshold: 5, circuitBreakerTimeout: Duration::fromMinutes(1), maxBulkErrorRate: 0.3, enableMetrics: true, enableTracing: false ); ``` ## Execution Strategies ### Standard Execution with Fallback ```php $result = $boundary->execute( operation: fn() => $service->riskyOperation(), fallback: fn() => $service->safeAlternative() ); ``` ### Optional Execution (Returns null on failure) ```php $result = $boundary->executeOptional( operation: fn() => $service->optionalOperation(), fallback: fn() => $service->defaultValue() // Optional fallback ); ``` ### Default Value on Failure ```php $result = $boundary->executeWithDefault( operation: fn() => $service->getValue(), defaultValue: 'default_value' ); ``` ### Result Wrapper ```php $result = $boundary->executeForResult( operation: fn() => $service->operation() ); if ($result->isSuccess()) { $value = $result->getValue(); } else { $error = $result->getError(); } ``` ## Retry Strategies ### Fixed Delay ```php RetryStrategy::FIXED // Same delay between retries ``` ### Linear Backoff ```php RetryStrategy::LINEAR // Linearly increasing delay ``` ### Exponential Backoff ```php RetryStrategy::EXPONENTIAL // Exponentially increasing delay ``` ### Exponential with Jitter ```php RetryStrategy::EXPONENTIAL_JITTER // Exponential + random jitter ``` ## Circuit Breaker Pattern ```php $config = new BoundaryConfig( circuitBreakerEnabled: true, circuitBreakerThreshold: 5, // Open after 5 failures circuitBreakerTimeout: Duration::fromMinutes(2) // Try again after 2 minutes ); $result = $boundary->executeWithCircuitBreaker( operation: fn() => $externalService->call(), fallback: fn() => $this->getCachedResponse() ); ``` ## Bulk Operations Handle batch processing with partial failure tolerance: ```php $items = [1, 2, 3, 4, 5]; $result = $boundary->executeBulk($items, function($item) { if ($item % 2 === 0) { throw new Exception("Even numbers fail"); } return $item * 2; }); echo "Processed: {$result->getProcessedCount()}/{$result->getTotalCount()}\n"; echo "Success rate: {$result->getSuccessRate()}%\n"; foreach ($result->getResults() as $key => $value) { echo "Item {$key}: {$value}\n"; } foreach ($result->getErrors() as $key => $error) { echo "Error {$key}: {$error->getMessage()}\n"; } ``` ## Timeout Protection ```php $result = $boundary->executeWithTimeout( operation: fn() => $longRunningService->process(), fallback: fn() => 'Operation timed out', timeoutSeconds: 30 ); ``` ## Parallel Operations Execute multiple operations with individual boundaries: ```php $operations = [ 'user_data' => fn() => $userService->getData(), 'preferences' => fn() => $prefsService->getPreferences(), 'notifications' => fn() => $notificationService->getCount() ]; $results = $boundary->executeParallel($operations); foreach ($results as $name => $result) { if ($result->isSuccess()) { $data[$name] = $result->getValue(); } else { $data[$name] = null; // Or default value } } ``` ## HTTP Middleware Integration Automatic error boundary protection for HTTP requests: ```php // In middleware registration $app->addMiddleware(ErrorBoundaryMiddleware::class); ``` The middleware automatically: - Creates boundaries based on route patterns - Provides JSON fallback responses for API routes - Provides HTML error pages for web routes - Logs failures for monitoring ## Environment Configuration Configure boundaries via environment variables: ```env # Global settings ERROR_BOUNDARY_ENABLED=true # Route-specific configuration ERROR_BOUNDARY_ROUTE_API_USER_MAX_RETRIES=5 ERROR_BOUNDARY_ROUTE_API_USER_CIRCUIT_BREAKER_ENABLED=true ERROR_BOUNDARY_ROUTE_API_USER_BASE_DELAY_MS=200 ``` ## Console Commands ### Test Error Boundaries ```bash # Test basic functionality php console.php boundary:test basic # Test retry strategies php console.php boundary:test retry # Test circuit breaker php console.php boundary:test circuit # Test bulk operations php console.php boundary:test bulk ``` ### Monitor Circuit Breakers ```bash # Show circuit breaker statistics php console.php boundary:stats # Reset specific circuit breaker php console.php boundary:reset user_service # Reset all circuit breakers php console.php boundary:reset ``` ## Best Practices ### 1. Choose Appropriate Configurations ```php // API endpoints - use external service config $apiBoundary = $factory->createForExternalService('payment_api'); // Database queries - use database config $dbBoundary = $factory->createForDatabase('user_queries'); // UI components - use UI config for fast failures $uiBoundary = $factory->createForUI('dashboard_widget'); ``` ### 2. Meaningful Fallbacks ```php // Good - provides useful fallback $boundary->execute( operation: fn() => $service->getLiveData(), fallback: fn() => $service->getCachedData() ); // Avoid - fallback provides no value $boundary->execute( operation: fn() => $service->getData(), fallback: fn() => null ); ``` ### 3. Monitor Circuit Breakers ```php // Set up alerting for circuit breaker state changes $boundary->executeWithCircuitBreaker( operation: fn() => $service->call(), fallback: function() { $this->logger->warning('Circuit breaker activated for service'); return $this->getFallbackData(); } ); ``` ### 4. Handle Bulk Operations Appropriately ```php $result = $boundary->executeBulk($items, $processor); // Check if too many failures occurred if ($result->getErrorRate() > 50) { $this->logger->error('High error rate in bulk operation'); // Consider stopping or alerting } ``` ## Integration Examples ### With Repositories ```php class UserRepository { public function __construct( private ErrorBoundaryFactory $boundaryFactory, private DatabaseConnection $db ) {} public function findById(int $id): ?User { $boundary = $this->boundaryFactory->createForDatabase('user_find'); return $boundary->executeOptional( operation: fn() => $this->db->query('SELECT * FROM users WHERE id = ?', [$id]), fallback: fn() => $this->getCachedUser($id) ); } } ``` ### With External APIs ```php class PaymentService { public function processPayment(Payment $payment): PaymentResult { $boundary = $this->boundaryFactory->createForExternalService('payment_gateway'); return $boundary->execute( operation: fn() => $this->gateway->process($payment), fallback: fn() => $this->queueForLaterProcessing($payment) ); } } ``` ### With Background Jobs ```php class EmailJob { public function handle(): void { $boundary = $this->boundaryFactory->createForBackgroundJob('email_sending'); $boundary->executeBulk($this->emails, function($email) { $this->mailer->send($email); }); } } ``` ## Error Handling Error boundaries can throw specific exceptions: - `BoundaryFailedException` - When both operation and fallback fail - `BoundaryTimeoutException` - When operations exceed timeout limits ```php try { $result = $boundary->execute($operation, $fallback); } catch (BoundaryFailedException $e) { $this->logger->error('Boundary failed completely', [ 'boundary' => $e->getBoundaryName(), 'original_error' => $e->getOriginalException()?->getMessage(), 'fallback_error' => $e->getFallbackException()?->getMessage(), ]); } catch (BoundaryTimeoutException $e) { $this->logger->warning('Operation timed out', [ 'boundary' => $e->getBoundaryName(), 'execution_time' => $e->getExecutionTime(), 'timeout_limit' => $e->getTimeoutLimit(), ]); } ``` ## Performance Considerations 1. **Circuit Breakers** - Use file-based storage for simplicity, consider Redis for high-traffic applications 2. **Retry Delays** - Use jitter to avoid thundering herd problems 3. **Bulk Operations** - Set appropriate error rate thresholds to prevent resource exhaustion 4. **Timeouts** - PHP's synchronous nature limits true timeout implementation ## Security Considerations 1. **Information Disclosure** - Ensure fallbacks don't leak sensitive information 2. **Resource Exhaustion** - Configure appropriate timeouts and retry limits 3. **Circuit Breaker State** - Protect circuit breaker state files from unauthorized access