# 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