ADR-0002. Kiến trúc phân lớp use-case (controller giữ mỏng)
| Trường | Giá trị |
|---|---|
| Status | Accepted |
| Ngày | 2026-04-05 |
| Người quyết định | support-team |
| Thay thế | — |
Bối cảnh
- Helpdesk có bề mặt lớn: 11 controller và hàng chục thao tác trải dài tickets, agents, SLA, articles, surveys, feature requests, và notifications.
- Đặt business logic trong controller (mẫu IGNIS phổ biến của fat service) sẽ tạo ra controller lan man và làm cùng một thao tác khó gọi từ cả handler HTTP và worker BullMQ (ví dụ
AutoAssignTicketUseCasechạy từ assignment worker,RunSlaMonitorUseCasetừ SLA worker).
Quyết định
Áp dụng một lớp use-case: mỗi thao tác nghiệp vụ là một lớp single-responsibility trong src/application/use-cases/<domain>/<verb>.use-case.ts, đăng ký làm service DI trong configureServices(). Cả controller và worker đều phụ thuộc vào use-case qua injection.
- Controller mỏng: thực thi truy cập theo merchant (
assertMerchantAccess()+useRequestContext()), ánh xạ DTO, gọi một use-case. Không có decorator@authenticate; truy cập được thực thi trong handler. - Service thuần (
PermissionService,CompensationCalculatorService,ProcessNotificationService, v.v.) giữ logic xuyên suốt dùng chung mà nhiều use-case tái dùng. - Repository chỉ giữ truy cập dữ liệu; schema tập trung trong
@nx/core.
Hệ quả
| Ưu | Nhược |
|---|---|
| Cùng thao tác tái dùng từ HTTP và worker (một đường code) | Nhiều file nhỏ — số lượng lớp cao để điều hướng |
| Single responsibility làm mỗi thao tác kiểm thử được độc lập | Danh sách đăng ký DI trong application.ts dài và phải giữ đồng bộ |
| Controller mỏng; ranh giới rõ giữa transport và logic | Rủi ro use-case mồ côi (một cuộc gọi bị comment out để assignTicketUseCase chết — xem Vận hành Vấn đề đã biết) |
Phương án đã cân nhắc
| Tùy chọn | Vì sao bị từ chối |
|---|---|
| Fat service (một service mỗi controller) | Khó chia sẻ một thao tác giữa điểm vào HTTP và worker; service phình to |
| Logic trong controller | Không tái dùng được từ worker; vi phạm hướng dẫn dự án về giữ logic ngoài controller |
Tham khảo
src/application/use-cases/**(lớp use-case)src/application.tsconfigureServices()(đăng ký DI)src/controllers/ticket/ticket.controller.ts(controller mỏng ủy thác cho use-case)AGENTS.md— "Keep business logic in use-cases, not controllers"