Kiến trúc Clean Architecture & Domain-Driven Design trong TypeScript
Phân tầng kiến trúc phần mềm rõ ràng giữa Domain Entities, Use Cases và Interface Adapters giúp mã nguồn dễ bảo trì, linh hoạt kiểm thử và độc lập hoàn toàn với Framework.
Khi ứng dụng ngày càng phát triển, sự phụ thuộc chặt chẽ vào một framework (như Next.js, Express hay NestJS) hoặc database (như Prisma, PostgreSQL) có thể biến codebase thành một 'bãi lầy' khó tái cấu trúc. Clean Architecture do Robert C. Martin (Uncle Bob) đề xuất cung cấp giải pháp phân tách ranh giới rõ ràng theo nguyên tắc Dependency Inversion.
1. Cấu trúc 4 lớp cơ bản
- Entities (Domain Layer): Chứa logic nghiệp vụ thuần túy, không import bất kỳ thư viện bên ngoài nào.
- Use Cases (Application Layer): Điều phối luồng dữ liệu đến và đi từ các entity.
- Interface Adapters: Chuyển đổi định dạng dữ liệu (Controllers, Presenters, DTOs).
- Frameworks & Drivers: Cơ sở dữ liệu, web framework, external APIs.
2. Quy tắc phụ thuộc một chiều (Dependency Rule)
Tất cả các mũi tên phụ thuộc đều hướng vào bên trong. Lớp Domain không biết Use Case là gì; lớp Use Case không biết Database là PostgreSQL hay MongoDB. Khi muốn đổi ORM từ Prisma sang Drizzle, bạn chỉ cần viết lại Repository Implementation mà không phải đụng vào một dòng code nghiệp vụ nào!
3. Bài học rút ra
Kiến trúc tốt không ngăn cản bạn viết mã nhanh trong giai đoạn đầu, nhưng nó cứu bạn hàng trăm giờ làm việc khi dự án bắt đầu mở rộng quy mô. Hãy bắt đầu từ việc thiết kế Interface rõ ràng trước khi cài đặt chi tiết.