Tìm hiểu cách thiết kế Exception Hierarchy trong PHP giúp xử lý lỗi theo tầng, clean architecture, dễ mở rộng. Kèm ví dụ, giải thích sâu, tips và câu hỏi phỏng vấn.
#Exception Hierarchy là gì?
Exception Hierarchy là việc tổ chức các exception theo dạng cây kế thừa (inheritance tree) thay vì để tất cả ở dạng “flat”.
👉 Mục tiêu:
- Cho phép catch theo nhiều level (broad → specific)
- Tái sử dụng logic xử lý lỗi
- Phản ánh đúng domain
#Bad Example (Anti-pattern)
class UserNotFoundException extends \Exception {}
class OrderNotFoundException extends \Exception {}
class ProductNotFoundException extends \Exception {}
class InvalidEmailException extends \Exception {}
class InvalidPriceException extends \Exception {}
class DatabaseConnectionException extends \Exception {}
class ApiTimeoutException extends \Exception {}
Vấn đề
- Không có quan hệ giữa các exception
- Không thể catch theo nhóm
- Vi phạm DRY (lặp lại logic xử lý)
- Khó mở rộng khi hệ thống lớn
👉 Caller phải catch từng exception riêng lẻ → code rối
#Good Example (Best Practice)
Base Exception
class AppException extends \RuntimeException {}
👉 Tất cả exception trong app nên extend từ đây
Nhóm NotFound
class NotFoundException extends AppException
{
public function __construct(
private string $entity,
private string|int $identifier,
) {
parent::__construct("{$entity} not found: {$identifier}");
}
}
class UserNotFoundException extends NotFoundException {}
class OrderNotFoundException extends NotFoundException {}
👉 Tất cả “not found” dùng chung logic
Nhóm Validation
class ValidationException extends AppException
{
public function __construct(private array $errors = [])
{
parent::__construct('Validation failed');
}
public function getErrors(): array
{
return $this->errors;
}
}
Nhóm Infrastructure
class InfrastructureException extends AppException {}
class DatabaseException extends InfrastructureException {}
class ExternalApiException extends InfrastructureException {}
👉 Tách rõ domain vs external system
Caller Handling
try {
$service->processOrder($data);
} catch (NotFoundException $e) {
return response()->json(['error' => $e->getMessage()], 404);
} catch (ValidationException $e) {
return response()->json(['errors' => $e->getErrors()], 422);
} catch (InfrastructureException $e) {
$logger->error($e->getMessage());
return response()->json(['error' => 'Service unavailable'], 503);
}
👉 Catch theo layer → cực kỳ clean
#Giải thích sâu
Layered Catching
Bạn có thể:
- Catch cụ thể (
UserNotFoundException) - Hoặc catch theo nhóm (
NotFoundException)
👉 Đây là sức mạnh của OOP
DRY Error Handling
Tất cả lỗi “not found”:
catch (NotFoundException $e)
👉 Không cần viết lại nhiều lần
Domain Modeling
Exception hierarchy = domain model
Ví dụ:
- NotFound → Business
- Validation → Input
- Infrastructure → External
👉 Đây là DDD mindset
Extensibility
Thêm:
class ProductNotFoundException extends NotFoundException {}
👉 Không cần sửa code cũ
RuntimeException vs Exception
👉 Luôn extend RuntimeException cho:
- Business errors
- External failures
#Tips & Tricks
Không để exception “flat”
👉 Luôn có base + phân nhóm
Phân tách rõ 3 layer
- Domain Exception
- Application Exception
- Infrastructure Exception
Logging chỉ ở Infrastructure
👉 Không log domain error như validation
Combine với Laravel Handler
Centralize toàn bộ xử lý exception
Không over-engineer
👉 Nhỏ thì không cần hierarchy phức tạp
#Interview Questions
1. Exception Hierarchy là gì và tại sao cần?
Summary:
- Là tổ chức exception theo cây kế thừa
- Giúp catch theo nhiều level
Deep: Exception hierarchy giúp:
- Group các lỗi liên quan
- Tái sử dụng logic
- Clean architecture
👉 Là best practice ở level senior
2. Flat exception có vấn đề gì?
Summary:
- Không grouping
- Code lặp
Deep: Flat exception khiến:
- Không thể catch theo nhóm
- Code bị duplication
- Khó maintain khi scale
3. Khi nào nên catch exception ở level cha?
Summary:
- Khi logic xử lý giống nhau
Deep: Ví dụ tất cả NotFound → trả 404
👉 Không cần catch từng loại
4. Có nên tạo quá nhiều exception không?
Summary:
- Không
Deep:
- Tạo khi có logic khác biệt
- Không tạo chỉ vì khác message
5. Exception hierarchy liên quan gì đến DDD?
Summary:
- Là domain language
Deep: Exception phản ánh business rule → là 1 phần của domain model
6. Làm sao design exception cho microservice?
Summary:
- Convert sang response
Deep:
- Không expose internal exception
- Dùng error code + message chuẩn hóa
7. Có nên catch Exception tổng không?
Summary:
- Có, nhưng ở boundary
Deep:
Chỉ catch Throwable ở:
- Controller
- Worker
👉 Không catch deep trong domain
#Kết luận
👉 Exception Hierarchy giúp:
- Clean code
- Scalable system
- Maintainable architecture
Nếu bạn vẫn đang dùng exception dạng flat → hệ thống sẽ rất khó scale