// drill 2026-05-17 Sun morning
5 bước · Time-box · Transition phrases · Mermaid diagrams · Self-test
| Bước | Time | Output |
|---|---|---|
| 1. CLARIFY | 5m | Functional + Non-functional + Scope |
| 2. API | 5m | Endpoints, request/response, auth, status codes |
| 3. DATA | 10m | Tables, FKs, indexes, SQL vs NoSQL choice |
| 4. ARCH | 5m | Box-and-arrow diagram, request flow |
| 5. TRADE-OFFS | 5-10m | Bottlenecks, scale strategy, failure modes |
Vẽ được diagram này từ đầu = pass baseline. Add component khác (CDN, S3, Elasticsearch, WebSocket) khi requirement đẩy lên.
Mỗi phrase có: (1) abstract template để memorize, (2) 2 ví dụ thực tế với scenario interview hay hỏi.
1️⃣ MỞ BÀI · sau khi interviewer give problem
"Trước khi em design, em xin clarify vài điểm: (1) MVP scope... (2) scale dự kiến...? (3) single hay multi-tenant? (4) consistency cần strong hay eventual?"
"Trước khi em design Task Management System, em xin clarify:
(1) MVP scope chỉ là CRUD task + assign cho user, hay cần sub-tasks, dependencies, recurring tasks?
(2) Scale dự kiến: bao nhiêu workspace, mỗi workspace bao nhiêu user concurrent? Em đoán 10k workspaces × 50 users là middle-tier.
(3) Multi-tenant — mỗi workspace cách ly hoàn toàn về data?
(4) Notification khi assign task: real-time WebSocket hay async qua queue cũng OK?
(5) Search/filter task: chỉ basic filter hay cần full-text search?"
"Trước khi em design Leave Request System, em xin clarify:
(1) Approval chain — chỉ manager duyệt, hay multi-level (manager → director → HR)?
(2) Leave types — annual, sick, unpaid riêng biệt? Có quota tracking không?
(3) Integration với calendar — Google/Outlook sync 2-way hay 1-way?
(4) Scale: bao nhiêu employee/công ty? Multi-company tenant?
(5) Audit trail — cần lưu lịch sử approve/reject để compliance không?"
2️⃣ CLARIFY → API · sau khi interviewer confirm scope
"OK với scope đó, đây là main endpoints em đề xuất. Pagination dùng cursor. Auth qua JWT trong Authorization header."
POST /api/workspaces/:wsId/tasks → 201 { id, ...task }
GET /api/workspaces/:wsId/tasks?cursor=&status=open → []task
GET /api/tasks/:id → 200 task
PATCH /api/tasks/:id → 200 task (status, assignee)
DELETE /api/tasks/:id → 204
POST /api/tasks/:id/comments → 201 comment
Auth: JWT trong Authorization: Bearer <token>
Multi-tenant: workspaceId trong path → check ACL ở middleware
"Em dùng cursor pagination thay offset vì list task có thể dài. Status codes: 201 cho POST, 204 cho DELETE. Validation qua Zod ở boundary."
POST /api/leave-requests → 201 { id, status: 'pending' }
GET /api/leave-requests?status=pending → []request (for managers)
GET /api/leave-requests/me → []request (employee's own)
POST /api/leave-requests/:id/approve → 200 { status: 'approved' }
POST /api/leave-requests/:id/reject → 200 { status: 'rejected', reason }
GET /api/leave-quotas/me → { annual: 12, used: 3, ... }
RBAC: employee tự CRUD own request. Manager approve/reject của team mình.
HR có quyền override mọi request.
3️⃣ API → DATA · sau khi list xong endpoints
"Để support API trên, đây là data model. Em chọn PostgreSQL vì schema ổn định, cần ACID cho [transaction part], và relationship-heavy."
workspaces (id PK, name, created_at)
users (id PK, email UNIQUE, name)
workspace_members (workspace_id FK, user_id FK, role) PK(ws_id, user_id)
tasks (
id UUID PK
workspace_id UUID FK [INDEX cho list-by-workspace]
assignee_id UUID FK [INDEX cho list-by-assignee]
status ENUM [INDEX composite (ws_id, status)]
due_date TIMESTAMP [INDEX cho "due soon" query]
title, description, created_at, updated_at
)
comments (id PK, task_id FK [INDEX], author_id FK, body, created_at)
"Em chọn PostgreSQL vì: (1) relationship-heavy với workspaces/users/tasks/comments, (2) cần JOINs để list 'my tasks across workspaces', (3) ACID cho transaction 'create task + send notification atomically'. Soft delete với deleted_at thay vì hard delete."
employees (id PK, email, manager_id FK→employees.id, dept_id FK)
leave_quotas (
employee_id FK, year INT, type ENUM, allotted INT, used INT
PRIMARY KEY (employee_id, year, type)
)
leave_requests (
id UUID PK
employee_id FK [INDEX]
type ENUM('annual','sick','unpaid')
start_date, end_date DATE [INDEX cho calendar query]
status ENUM('pending','approved','rejected','cancelled') [INDEX]
approver_id FK NULL, decided_at TIMESTAMP NULL, reason TEXT
created_at
)
leave_audit (id PK, request_id FK, actor_id, action, at) -- compliance log
"PostgreSQL với daterange type cho [start, end] range queries. Transaction khi approve: update status + decrement quota + insert audit log — phải atomic."
4️⃣ DATA → ARCH · sau khi sketch schema
"Đây là architecture high-level. Em sẽ trace flow cho 1 request POST: client → LB → API → write DB → invalidate cache → async worker push notification qua queue."
"Em trace flow cho POST /tasks (create task with assignee):
1. Client gửi request lên Load Balancer (nginx/ELB)
2. LB route tới 1 trong 3 API server (NestJS, stateless)
3. Middleware: verify JWT → check workspace ACL → validate body với Zod
4. Service: BEGIN TX → insert task → publish event to BullMQ queue → COMMIT
5. Invalidate Redis cache cho key tasks:ws:<id>:list
6. Return 201 với task object — request đã xong (~50ms)
7. Async: Worker pick event từ queue → fetch assignee → send email + push noti + create in-app notification record"
"Em trace flow cho POST /leave-requests/:id/approve:
1. Manager gửi request → LB → API server
2. Auth: verify JWT + check RBAC (must be approver_id hoặc HR)
3. BEGIN TX:
a. SELECT request FOR UPDATE — lock row tránh race
b. Check status === 'pending' (idempotency)
c. UPDATE status = 'approved', decided_at, approver_id
d. UPDATE leave_quotas SET used = used + days
e. INSERT vào leave_audit
4. COMMIT TX
5. Publish event → Worker push notification cho employee + sync to Google Calendar
6. Return 200"
5️⃣ ARCH → TRADE-OFFS · sau khi vẽ diagram
"Bottleneck đầu tiên sẽ là DB writes. Em optimize qua 3 step: (1) Redis cache cho hot reads, (2) read replicas khi reads dominate, (3) shard theo workspace_id nếu writes triệu/giờ."
"Phân tích scale cho 100k workspaces × 50 users avg:
Bottleneck #1: List task query với pagination + filter — chiếm 70% traffic.
→ Solution: Redis cache key tasks:ws:<id>:status:open TTL 60s. Invalidate on write.
Bottleneck #2: Notification storm khi assign batch task.
→ Solution: Rate-limit notification per assignee, batch 5 task / 5 phút thành 1 email digest.
Failure mode: Redis down → fallback DB (slower nhưng works). Worker queue backlog → scale workers horizontally.
Em đánh đổi: Cache invalidation complexity để có read latency thấp. Strong consistency trên write path, eventual cho notification delivery (OK trong context này)."
"Phân tích cho mid-size company 1k-10k employees:
Bottleneck: Không phải write throughput (leave request thấp). Vấn đề là race condition khi 2 manager cùng approve / employee cancel khi đang approve.
→ Solution: Row-level lock với SELECT ... FOR UPDATE trong transaction. Hoặc optimistic locking với version column.
Consistency: Quota update phải ATOMIC với approve (cùng TX). Nếu fail giữa chừng, rollback toàn bộ.
Calendar sync failure: Async qua queue với retry exponential backoff. Nếu Google Calendar API down → vẫn approve, sync sau.
Em đánh đổi: Throughput thấp (write < 100/giây) để có strict consistency — vì leave quota là business-critical, không được tính sai."
6️⃣ KẾT BÀI · sau ~30-40 phút design
"Tóm lại em đã chọn [X, Y, Z]. Em đánh đổi [A] để có [B]. Open to discuss alternatives nếu anh/chị muốn em design lại theo direction khác."
"Tóm lại em chọn:
• PostgreSQL multi-tenant với workspace_id ACL ở mọi tầng
• Redis cache cho list query — invalidate on write
• BullMQ cho notification async — không block API request
• Stateless API → scale horizontal qua load balancer
Đánh đổi: cache invalidation complexity để có read latency thấp. Eventual consistency cho notification (OK vì user không expect realtime <100ms).
Open to discuss: nếu cần realtime collaboration (như Notion), em sẽ thêm WebSocket layer + CRDT cho concurrent edit. Anh/chị có muốn em đi sâu hướng đó?"
"Tóm lại em chọn:
• PostgreSQL với daterange type + row-level lock cho approve flow
• Strong consistency trên quota update — không cache
• Async queue cho calendar sync (retryable, không block approve)
• Audit log table immutable cho compliance
Đánh đổi: throughput thấp hơn (do row-level lock) để có zero quota miscalculation — business-critical.
Open to discuss: nếu công ty rất lớn (50k+ employees), em sẽ shard theo company_id và partition leave_audit table theo year để giảm size."
→ CLARIFY · API · DATA · ARCH · TRADE-OFFS
→ Functional · Non-functional (scale, latency, consistency) · Scope (auth, mobile, multi-tenant)
→ Conflict (duplicate insert, race condition, optimistic lock fail)
→ Schema ổn · JOINs phức tạp · ACID transactions critical · Relationship-heavy
→ (1) Redis cache hot reads (2) Read replicas khi reads dominate (3) Shard theo workspace_id nếu writes scale lên triệu/giờ
→ "Trước khi em design, em xin clarify..."
System Design KHÔNG test bạn biết bao nhiêu term — test bạn cấu trúc tư duy. Học thuộc 5 bước → đi đúng thứ tự → không mất điểm "process" → tự do tập trung vào content. Mỗi quyết định = nói trade-off. Im lặng > 30s = mất điểm.