# Developer Migration Guide Step-by-step guide for migrating modules to the new exception hierarchy system. ## Overview This guide provides a practical, hands-on approach to migrating existing modules from legacy exception handling (RuntimeException, generic exceptions) to the framework's standardized exception hierarchy with ErrorCode integration. ## Prerequisites Before starting migration: - Read [Exception Hierarchy Pattern Guide](./exception-hierarchy-pattern.md) - Read [ErrorHandler Enhancements Guide](./error-handler-enhancements.md) - Understand ErrorCode system and ExceptionContext - Have module test suite ready for validation ## Migration Process Overview ``` 1. Module Analysis → Identify exceptions and patterns 2. ErrorCode Definition → Create module-specific error codes 3. Base Exception → Create module base exception 4. Specific Exceptions → Create domain-specific exceptions 5. Service Migration → Replace legacy exceptions 6. Testing & Validation → Verify functionality 7. Documentation Update → Update module docs ``` ## Step 1: Module Analysis ### Identify All Exceptions Use grep to find all exceptions in the module: ```bash # Find all RuntimeException throws grep -r "throw new \\\\RuntimeException" src/Framework/YourModule/ # Find all InvalidArgumentException (business logic only) grep -r "throw new \\\\InvalidArgumentException" src/Framework/YourModule/ # Find all generic Exception throws grep -r "throw new \\\\Exception" src/Framework/YourModule/ # Create inventory grep -r "throw new" src/Framework/YourModule/ | wc -l ``` **Example Output from Queue Module**: ``` Found 22 exceptions: - 15 RuntimeException (business logic issues) - 5 InvalidArgumentException (mix of constructor validation and business logic) - 2 Exception (generic errors) ``` ### Categorize Exceptions Create a spreadsheet or document: | File | Line | Exception Type | Context | Keep/Migrate | |------|------|----------------|---------|--------------| | JobPersistenceLayer.php | 123 | RuntimeException | Job not found | **MIGRATE** | | JobChainExecutionCoordinator.php | 45 | InvalidArgumentException | Empty chain ID in constructor | **KEEP** | | StepProgressTracker.php | 43 | RuntimeException | All steps completed | **MIGRATE** | **Decision Rules**: - **MIGRATE**: Business logic exceptions, state violations, not found errors - **KEEP**: Constructor parameter validation, type validation, invalid arguments in constructors ### Identify Common Patterns Group exceptions by scenario: ``` Entity Not Found: JobNotFoundException, ChainNotFoundException, WorkerNotFoundException State Violations: InvalidChainStateException, AllStepsCompletedException Infrastructure: RedisExtensionNotLoadedException ``` ## Step 2: ErrorCode Definition ### Create Module ErrorCode Enum **Location**: `src/Framework/Exception/Core/{Module}ErrorCode.php` **Template**: ```php value; } public function getCategory(): string { return 'MODULE'; } public function getNumericCode(): int { return (int) substr($this->value, -3); } public function getSeverity(): ErrorSeverity { return match($this) { self::ENTITY_NOT_FOUND => ErrorSeverity::ERROR, self::RESOURCE_MISSING => ErrorSeverity::ERROR, self::INVALID_STATE => ErrorSeverity::WARNING, self::OPERATION_NOT_ALLOWED => ErrorSeverity::WARNING, self::DEPENDENCY_MISSING => ErrorSeverity::CRITICAL, self::SERVICE_UNAVAILABLE => ErrorSeverity::ERROR, self::VALIDATION_FAILED => ErrorSeverity::WARNING, self::CONSTRAINT_VIOLATION => ErrorSeverity::ERROR, }; } public function getDescription(): string { return match($this) { self::ENTITY_NOT_FOUND => 'Requested entity not found', self::RESOURCE_MISSING => 'Required resource is missing', self::INVALID_STATE => 'Operation not allowed in current state', self::OPERATION_NOT_ALLOWED => 'Operation not permitted', self::DEPENDENCY_MISSING => 'Required dependency not available', self::SERVICE_UNAVAILABLE => 'Service temporarily unavailable', self::VALIDATION_FAILED => 'Input validation failed', self::CONSTRAINT_VIOLATION => 'Business constraint violated', }; } public function getRecoveryHint(): string { return match($this) { self::ENTITY_NOT_FOUND => 'Verify the entity ID exists in the system', self::RESOURCE_MISSING => 'Check if required resources are configured', self::INVALID_STATE => 'Check entity state and ensure prerequisites are met', self::OPERATION_NOT_ALLOWED => 'Verify permissions and operation context', self::DEPENDENCY_MISSING => 'Install required dependency or use fallback', self::SERVICE_UNAVAILABLE => 'Retry operation after brief delay', self::VALIDATION_FAILED => 'Review input data and correct validation errors', self::CONSTRAINT_VIOLATION => 'Adjust operation to meet business constraints', }; } public function isRecoverable(): bool { return match($this) { self::DEPENDENCY_MISSING => false, default => true, }; } public function getRetryAfterSeconds(): ?int { return match($this) { self::SERVICE_UNAVAILABLE => 60, default => null, }; } } ``` **Real-World Example from Queue Module**: ```php enum QueueErrorCode: string implements ErrorCode { case JOB_NOT_FOUND = 'QUEUE007'; case CHAIN_NOT_FOUND = 'QUEUE008'; case INVALID_STATE = 'QUEUE009'; case WORKER_UNAVAILABLE = 'QUEUE010'; // ... implementations } ``` ### ErrorCode Naming Conventions - **Category Prefix**: 2-4 uppercase letters (QUEUE, DB, AUTH, VAL) - **Numeric Suffix**: 3 digits (001-999) - **Severity Mapping**: CRITICAL for system failures, ERROR for operation failures, WARNING for state issues - **Recovery**: true for temporary issues, false for permanent failures - **Retry**: Set seconds only for infrastructure issues (DB connection, network, etc.) ## Step 3: Create Module Base Exception **Location**: `src/Framework/{Module}/Exceptions/{Module}Exception.php` **Template**: ```php database->query(/* ... */); if ($user === null) { throw new \RuntimeException("User with ID {$id->toString()} not found"); } return $user; } ``` **After** (Framework-Compliant): ```php // New file: src/Framework/YourModule/Exceptions/UserNotFoundException.php withData([ 'user_id' => $id->toString(), 'search_type' => 'by_id', ]); return self::create( YourModuleErrorCode::ENTITY_NOT_FOUND, "User with ID '{$id->toString()}' not found", $context ); } public static function byEmail(Email $email): self { $context = ExceptionContext::forOperation('user.lookup', 'UserRepository') ->withData([ 'email' => $email->getMasked(), 'search_type' => 'by_email', ]); return self::create( YourModuleErrorCode::ENTITY_NOT_FOUND, "User with email not found", $context ); } } ``` ### Pattern 2: State Violation Exception **Before** (Legacy): ```php // In OrderProcessor.php public function cancel(Order $order): void { if ($order->status === OrderStatus::SHIPPED) { throw new \RuntimeException('Cannot cancel shipped order'); } // Cancel logic } ``` **After** (Framework-Compliant): ```php // New file: src/Framework/YourModule/Exceptions/OrderAlreadyShippedException.php withData([ 'order_id' => $orderId, 'current_status' => 'shipped', 'operation' => 'cancel', ]); return self::create( YourModuleErrorCode::INVALID_STATE, "Cannot cancel order '{$orderId}' - order already shipped", $context ); } } ``` ### Pattern 3: Infrastructure Exception **Before** (Legacy): ```php // In ServiceInitializer.php public function initialize(): Service { if (!extension_loaded('redis')) { throw new \RuntimeException('Redis extension not loaded'); } return new RedisService(); } ``` **After** (Framework-Compliant): ```php // New file: src/Framework/YourModule/Exceptions/RedisExtensionNotLoadedException.php withData([ 'required_extension' => 'redis', 'loaded_extensions' => get_loaded_extensions(), 'fallback_available' => 'FileService', ]); return self::fromContext( 'Redis PHP extension is not loaded', $context, YourModuleErrorCode::DEPENDENCY_MISSING ); } } ``` ## Step 5: Service Migration ### Migration Checklist for Each File - [ ] Identify all legacy exception throws - [ ] Determine which exceptions to keep (constructor validation) - [ ] Create or reuse specific exception classes - [ ] Replace RuntimeException throws with specific exceptions - [ ] Add use statements for new exception classes - [ ] Update error messages to be more descriptive - [ ] Add relevant context data - [ ] Test each replaced exception ### Migration Example: Complete File **Before** - `StepProgressTracker.php`: ```php jobId)) { throw new \InvalidArgumentException('Job ID cannot be empty'); } if (empty($this->steps)) { throw new \InvalidArgumentException('Steps array cannot be empty'); } } public function completeCurrentStep(): void { $progress = $this->progressTracker->getProgress($this->jobId); $currentStepIndex = $progress->current_step; if ($currentStepIndex >= count($this->steps)) { throw new \RuntimeException('All steps have already been completed'); } // Complete step logic } public function updateStepProgress(int $percentage): void { $progress = $this->progressTracker->getProgress($this->jobId); $currentStepIndex = $progress->current_step; if ($currentStepIndex >= count($this->steps)) { throw new \RuntimeException('All steps have already been completed'); } // Update logic } } ``` **After** - `StepProgressTracker.php`: ```php jobId)) { throw new \InvalidArgumentException('Job ID cannot be empty'); } if (empty($this->steps)) { throw new \InvalidArgumentException('Steps array cannot be empty'); } } public function completeCurrentStep(): void { $progress = $this->progressTracker->getProgress($this->jobId); $currentStepIndex = $progress->current_step; if ($currentStepIndex >= count($this->steps)) { // MIGRATED: Business logic exception throw AllStepsCompletedException::forJob($this->jobId, count($this->steps)); } // Complete step logic } public function updateStepProgress(int $percentage): void { $progress = $this->progressTracker->getProgress($this->jobId); $currentStepIndex = $progress->current_step; if ($currentStepIndex >= count($this->steps)) { // MIGRATED: Business logic exception throw AllStepsCompletedException::forJob($this->jobId, count($this->steps)); } // Update logic } } ``` **Changes Made**: 1. Added `use App\Framework\Queue\Exceptions\AllStepsCompletedException;` 2. Kept constructor InvalidArgumentException throws (parameter validation) 3. Replaced both RuntimeException throws with `AllStepsCompletedException::forJob()` 4. More descriptive error messages via factory method 5. Rich context automatically added by exception ## Step 6: Testing & Validation ### Create Test File **Location**: `tests/debug/test-{module}-exception-migration.php` ```php getMessage() . "\n"; echo "Error Code: " . $e->getErrorCode()->getValue() . "\n"; echo "Category: " . $e->getErrorCode()->getCategory() . "\n"; echo "Severity: " . $e->getErrorCode()->getSeverity()->value . "\n"; echo "Is Recoverable: " . ($e->getErrorCode()->isRecoverable() ? 'Yes' : 'No') . "\n"; echo "Recovery Hint: " . $e->getErrorCode()->getRecoveryHint() . "\n"; } echo "\n" . str_repeat('=', 60) . "\n\n"; // Test 2: State Violation Exception echo "Test 2: State Violation Exception\n"; echo str_repeat('-', 60) . "\n"; // ... test state violation echo "\n✅ All migration tests completed!\n"; ``` ### Run Test Suite ```bash # Run debug test php tests/debug/test-yourmodule-exception-migration.php # Run module test suite ./vendor/bin/pest tests/Unit/Framework/YourModule/ # Verify no regressions ./vendor/bin/pest ``` ### Validate ErrorHandler Integration ```php // Test ErrorHandler with new exceptions use App\Framework\ErrorHandling\ErrorHandler; use App\Framework\DI\DefaultContainer; use App\Framework\Http\ResponseEmitter; use App\Framework\Http\RequestIdGenerator; $container = new DefaultContainer(); $emitter = new ResponseEmitter(); $requestIdGenerator = new RequestIdGenerator(); $errorHandler = new ErrorHandler( $emitter, $container, $requestIdGenerator, null, true // Debug mode to see recovery hints ); try { throw EntityNotFoundException::byId($entityId); } catch (EntityNotFoundException $e) { $response = $errorHandler->createHttpResponse($e); // Verify HTTP status based on category echo "HTTP Status: " . $response->status->value . "\n"; // Verify metadata includes ErrorCode info // Check response body for recovery hints (debug mode) } ``` ## Step 7: Documentation Update ### Update Module Documentation Add section to module's documentation: ```markdown ## Exception Handling This module uses framework-compliant exception hierarchies. **Module Exceptions**: - `EntityNotFoundException` - Thrown when entity not found (ENTITY_NOT_FOUND) - `InvalidStateException` - Thrown for state violations (INVALID_STATE) - `DependencyMissingException` - Thrown for missing dependencies (DEPENDENCY_MISSING) **Error Codes**: - `MODULE001` - Entity not found (ERROR) - `MODULE011` - Invalid state (WARNING) - `MODULE021` - Dependency missing (CRITICAL) **Usage Example**: \`\`\`php try { $entity = $this->repository->find($id); } catch (EntityNotFoundException $e) { // Handle not found $this->logger->error('Entity not found', [ 'error_code' => $e->getErrorCode()->getValue(), 'entity_id' => $id->toString() ]); return $this->notFoundResponse($e->getMessage()); } \`\`\` ``` ## Migration Strategy by Module Size ### Small Modules (<10 exceptions) - **Approach**: Migrate all at once in single session - **Time**: 1-2 hours - **Steps**: All 7 steps in sequence - **Example**: Vault module (4 exceptions) ### Medium Modules (10-30 exceptions) - **Approach**: Migrate by service/component - **Time**: 2-4 hours - **Steps**: Multiple sessions, one component at a time - **Example**: Queue module (22 exceptions) ### Large Modules (>30 exceptions) - **Approach**: Migrate by subdomain or priority - **Time**: 4-8 hours - **Steps**: Multiple days, prioritize critical paths - **Example**: Database module (38 exceptions) ## Common Pitfalls & Solutions ### Pitfall 1: Keeping vs. Migrating InvalidArgumentException **Problem**: Unclear when to keep InvalidArgumentException **Solution**: ```php // ✅ KEEP - Constructor parameter validation public function __construct(string $id) { if (empty($id)) { throw new \InvalidArgumentException('ID cannot be empty'); } } // ❌ MIGRATE - Business logic validation public function process(Order $order): void { if ($order->items->isEmpty()) { // Should be OrderEmptyException throw new \InvalidArgumentException('Order has no items'); } } ``` ### Pitfall 2: Over-Generic Exception Messages **Problem**: Messages too generic, not actionable **Solution**: ```php // ❌ Bad throw new \RuntimeException('Operation failed'); // ✅ Good throw OperationFailedException::forPayment( $paymentId, 'Gateway timeout after 30s' ); ``` ### Pitfall 3: Missing Context Data **Problem**: Exception thrown without context **Solution**: ```php // ❌ Bad throw EntityNotFoundException::byId($id); // Context auto-generated but minimal // ✅ Good $context = ExceptionContext::forOperation('entity.delete', 'EntityService') ->withData([ 'entity_id' => $id->toString(), 'entity_type' => 'Product', 'operation' => 'delete', 'user_id' => $currentUser->id, ]) ->withDebug([ 'query' => $query, 'execution_time_ms' => $executionTime, ]); throw EntityNotFoundException::fromContext( "Product with ID '{$id->toString()}' not found", $context, YourModuleErrorCode::ENTITY_NOT_FOUND ); ``` ### Pitfall 4: Wrong ErrorCode Severity **Problem**: Severity doesn't match actual impact **Solution**: ```php // ❌ Bad - State violation marked as CRITICAL ErrorSeverity::CRITICAL // Will page ops at 3am // ✅ Good - State violation is WARNING ErrorSeverity::WARNING // Logged but not paged ``` ### Pitfall 5: Creating Too Many Exception Classes **Problem**: One exception class per scenario **Solution**: ```php // ❌ Bad - Too many classes UserNotFoundByIdException UserNotFoundByEmailException UserNotFoundByUsernameException // ✅ Good - One class with factory methods final class UserNotFoundException { public static function byId(UserId $id): self { } public static function byEmail(Email $email): self { } public static function byUsername(string $username): self { } } ``` ## Real-World Migration: Queue Module ### Module Statistics - **Files Analyzed**: 15 service files - **Total Exceptions Found**: 22 exceptions - **Exceptions Migrated**: 22 (100%) - **Exception Classes Created**: 7 - **Error Codes Added**: 4 - **Services Migrated**: 6 - **Migration Time**: ~3 hours ### Exception Classes Created 1. `JobNotFoundException` - Job not found by ID or in queue 2. `ChainNotFoundException` - Chain not found by ID or name 3. `InvalidChainStateException` - Chain not in correct state 4. `CircularDependencyException` - Circular dependency detected 5. `AllStepsCompletedException` - All steps already completed 6. `RedisExtensionNotLoadedException` - Redis extension missing 7. `WorkerNotFoundException` - Worker not registered ### Error Codes Added ```php enum QueueErrorCode: string implements ErrorCode { case JOB_NOT_FOUND = 'QUEUE007'; case CHAIN_NOT_FOUND = 'QUEUE008'; case INVALID_STATE = 'QUEUE009'; case WORKER_UNAVAILABLE = 'QUEUE010'; } ``` ### Migration Order 1. **Day 1**: Analysis + ErrorCode definition (1h) 2. **Day 1**: Base exception + 3 entity not found exceptions (1h) 3. **Day 2**: State violation exceptions (0.5h) 4. **Day 2**: Service migrations (0.5h) 5. **Day 2**: Testing + validation (0.5h) ### Lessons Learned - Start with most common exception patterns (not found, state violation) - Migrate similar exceptions together (all not found, then all state) - Test incrementally after each exception class created - Constructor InvalidArgumentException are always kept - Factory method naming: `byId()`, `forJob()`, `notPending()` ## Quick Reference Checklist ### Pre-Migration - [ ] Read pattern guide and enhancement guide - [ ] Analyze module and count exceptions - [ ] Categorize exceptions (keep vs. migrate) - [ ] Identify common patterns ### ErrorCode Creation - [ ] Create `{Module}ErrorCode.php` in `Exception/Core/` - [ ] Define all error codes with proper numbering - [ ] Implement all required methods (getValue, getCategory, etc.) - [ ] Set appropriate severity levels - [ ] Add recovery hints for recoverable errors - [ ] Set retry seconds for infrastructure errors ### Exception Classes - [ ] Create `{Module}Exception.php` base class - [ ] Create specific exception classes in `{Module}/Exceptions/` - [ ] Use factory methods instead of constructors - [ ] Build rich ExceptionContext - [ ] Choose appropriate ErrorCode ### Service Migration - [ ] Keep constructor InvalidArgumentException - [ ] Replace business logic RuntimeException - [ ] Add use statements for new exceptions - [ ] Improve error messages - [ ] Add context data ### Testing - [ ] Create debug test file - [ ] Run module test suite - [ ] Verify ErrorHandler integration - [ ] Check HTTP status codes - [ ] Validate recovery hints (debug mode) ### Documentation - [ ] Update module documentation - [ ] Add exception handling section - [ ] Document error codes - [ ] Provide usage examples ## Next Steps After Migration Once your module is migrated: 1. **Monitor Production**: Watch for any regression issues 2. **Collect Feedback**: Get team feedback on error clarity 3. **Iterate**: Refine error messages based on feedback 4. **Share Learnings**: Update this guide with lessons learned 5. **Help Others**: Assist with other module migrations ## Support & Resources - **Pattern Guide**: [exception-hierarchy-pattern.md](./exception-hierarchy-pattern.md) - **Enhancement Guide**: [error-handler-enhancements.md](./error-handler-enhancements.md) - **ErrorCode Reference**: See `src/Framework/Exception/Core/` directory - **Example Module**: Queue module (`src/Framework/Queue/`) ## Framework Exception Migrations (Completed) The framework's core exceptions have been successfully migrated to the new exception hierarchy system. ### Migration Summary **Total Time**: ~6 hours across multiple phases **Exception Classes Migrated**: 40+ framework exceptions **ErrorCode Enums Created**: 5 category-specific enums **Test Coverage**: 100% integration test pass rate ### Phase 1: ErrorCode Enum Creation **Created Category-Specific Error Code Enums**: 1. **DatabaseErrorCode** (`src/Framework/Exception/Core/DatabaseErrorCode.php`) - 8 cases covering connection, query, transaction, and schema errors - Severity: CONNECTION_FAILED (CRITICAL), QUERY_FAILED (ERROR), etc. - Recovery: Retry logic for temporary issues, non-recoverable for constraint violations 2. **AuthErrorCode** (`src/Framework/Exception/Core/AuthErrorCode.php`) - 10 cases covering authentication and authorization - Severity: TOKEN_EXPIRED (WARNING), ACCOUNT_LOCKED (ERROR), etc. - Recovery: Most auth errors are non-recoverable, require user action 3. **HttpErrorCode** (`src/Framework/Exception/Core/HttpErrorCode.php`) - 8 cases covering HTTP protocol errors - Severity: BAD_REQUEST (WARNING), INTERNAL_SERVER_ERROR (CRITICAL) - Recovery: Client errors non-recoverable, server errors may retry 4. **SecurityErrorCode** (`src/Framework/Exception/Core/SecurityErrorCode.php`) - 10 cases covering security threats and attacks - Severity: SQL_INJECTION_DETECTED (CRITICAL), CSRF_TOKEN_INVALID (ERROR) - Recovery: Security violations are never recoverable 5. **ValidationErrorCode** (`src/Framework/Exception/Core/ValidationErrorCode.php`) - 8 cases covering input and business validation - Severity: INVALID_INPUT (WARNING), BUSINESS_RULE_VIOLATION (ERROR) - Recovery: Validation errors recoverable with corrected input ### Phase 2: Database Exception Migration **Migrated Exceptions**: - `DatabaseConnectionException` → DatabaseErrorCode::CONNECTION_FAILED - `QueryExecutionException` → DatabaseErrorCode::QUERY_FAILED - `TransactionException` → DatabaseErrorCode::TRANSACTION_FAILED - `EntityNotFoundException` → DatabaseErrorCode::ENTITY_NOT_FOUND - `ConstraintViolationException` → DatabaseErrorCode::CONSTRAINT_VIOLATION **Example Migration**: ```php // Before throw new \RuntimeException("Database connection failed"); // After throw DatabaseConnectionException::connectionRefused( $host, $port, $previous ); // Uses DatabaseErrorCode::CONNECTION_FAILED with rich context ``` ### Phase 3.2: Authentication/Authorization Exception Migration **Migrated Exceptions**: - `InvalidCredentialsException` → AuthErrorCode::CREDENTIALS_INVALID - `TokenExpiredException` → AuthErrorCode::TOKEN_EXPIRED - `SessionExpiredException` → AuthErrorCode::SESSION_EXPIRED - `AccountLockedException` → AuthErrorCode::ACCOUNT_LOCKED - `InsufficientPermissionsException` → AuthErrorCode::INSUFFICIENT_PERMISSIONS **Example Migration**: ```php // Before throw new \RuntimeException("Invalid credentials"); // After throw InvalidCredentialsException::forUser($username); // Uses AuthErrorCode::CREDENTIALS_INVALID with security context ``` ### Phase 3.3: HTTP Exception Migration **Migrated Exceptions**: - `BadRequestException` → HttpErrorCode::BAD_REQUEST - `NotFoundException` → HttpErrorCode::NOT_FOUND - `MethodNotAllowedException` → HttpErrorCode::METHOD_NOT_ALLOWED - `InternalServerErrorException` → HttpErrorCode::INTERNAL_SERVER_ERROR **Example Migration**: ```php // Before throw new \RuntimeException("Resource not found"); // After throw NotFoundException::forResource($resourceType, $resourceId); // Uses HttpErrorCode::NOT_FOUND with HTTP context ``` ### Phase 3.4: Security Exception Migration **Migrated Exceptions** (OWASP-Compliant): - `CsrfValidationFailedException` → SecurityErrorCode::CSRF_TOKEN_INVALID - `SqlInjectionAttemptException` → SecurityErrorCode::SQL_INJECTION_DETECTED - `XssAttemptException` → SecurityErrorCode::XSS_DETECTED - `PathTraversalAttemptException` → SecurityErrorCode::PATH_TRAVERSAL_DETECTED **Example Migration**: ```php // Before throw new \RuntimeException("CSRF token validation failed"); // After throw CsrfValidationFailedException::tokenValidationFailed($formId); // Uses SecurityErrorCode::CSRF_TOKEN_INVALID with security event logging ``` **Security Features**: - Automatic OWASP security event logging - Attack pattern analysis (SQL injection types, XSS vectors) - IOC (Indicator of Compromise) generation - WAF rule suggestions - Threat severity assessment ### Phase 3.5: Validation Exception Migration **Result**: No validation exceptions found using old ErrorCode constants. All validation exceptions in the framework already use the new ValidationErrorCode enum pattern. ### Migration Benefits Achieved **1. Type Safety** ```php // Before: Generic catch catch (\RuntimeException $e) { } // After: Specific exception handling catch (DatabaseConnectionException $e) { // Handle database connection specifically $this->fallbackToCache(); } ``` **2. Rich Error Context** ```php // Automatic context includes: [ 'operation' => 'user.login', 'component' => 'AuthService', 'error_code' => 'AUTH002', 'category' => 'AUTH', 'severity' => 'ERROR', 'timestamp' => '2024-01-15 14:32:10', 'request_id' => 'req_abc123', // + custom data ] ``` **3. Category-Based Monitoring** ```php // Monitor by category if ($exception->isCategory('DB')) { $this->metricsCollector->incrementDatabaseErrors(); } // Check specific error code if ($exception->isErrorCode(DatabaseErrorCode::CONNECTION_FAILED)) { $this->alertOps('Database connection down'); } ``` **4. Automatic HTTP Status Mapping** ```php // ErrorHandler automatically maps: DatabaseErrorCode::ENTITY_NOT_FOUND → 404 Not Found AuthErrorCode::CREDENTIALS_INVALID → 401 Unauthorized HttpErrorCode::BAD_REQUEST → 400 Bad Request SecurityErrorCode::CSRF_TOKEN_INVALID → 403 Forbidden ValidationErrorCode::INVALID_INPUT → 422 Unprocessable Entity ``` **5. Recovery Hints** ```php // ErrorCode provides recovery guidance $errorCode->getRecoveryHint(); // "Check database connection and retry operation" $errorCode->isRecoverable(); // true/false $errorCode->getRetryAfterSeconds(); // 60 for temporary issues ``` ### Testing Results **Integration Tests**: 100% Pass Rate ```bash ./vendor/bin/pest tests/Unit/ErrorHandling/ErrorHandlerFullPipelineTest.php ✓ it handles database connection exceptions correctly ✓ it handles authentication exceptions with proper status codes ✓ it handles HTTP exceptions with correct status mapping ✓ it handles security exceptions with OWASP event logging ✓ it provides recovery hints in debug mode 5 tests passed, 26 assertions ``` ### Next Steps **Recommended Migrations** (Optional): 1. **Domain Module Exceptions**: Migrate domain-specific exceptions (User, Order, etc.) 2. **Infrastructure Exceptions**: Migrate external service exceptions (Email, Storage, etc.) 3. **Application Exceptions**: Migrate controller and API exceptions **Maintenance**: - Monitor error logs for any regressions - Update error messages based on user feedback - Add new ErrorCode cases as needed for new features - Keep exception tests up to date ## Conclusion Exception migration improves: - ✅ **Error Clarity**: Descriptive messages with context - ✅ **Type Safety**: Catch specific exceptions, not generic ones - ✅ **Debugging**: Rich context for troubleshooting - ✅ **Monitoring**: Category-based alerting and metrics - ✅ **Recovery**: Automatic retry strategies and hints - ✅ **Documentation**: Self-documenting exception hierarchy - ✅ **HTTP Status**: Automatic status code mapping - ✅ **Logging**: Severity-based log levels Take your time, test thoroughly, and don't hesitate to refactor as you learn better patterns!