HARRY-19 — Index dự án — 1 codebase, 5 lần nâng cấp
Index dự án — 1 codebase, 5 lần nâng cấp
Trang tổng hợp mọi dự án trong lộ trình. Spec chi tiết nằm trong từng giai đoạn; đây là nơi xem toàn cảnh, thứ tự phụ thuộc, và tiến độ.
Nguyên tắc xuyên suốt: không làm 5 dự án rời rạc. Bạn nâng cấp cùng một codebase qua từng giai đoạn. Đó là điều khiến portfolio của bạn khác với 500 repo "todo-app" khác trên GitHub — người phỏng vấn nhìn thấy một hệ thống tiến hoá có lịch sử git thật, không phải một bài tutorial chép lại.
Bản đồ
DA0 Skeleton TS (GĐ1) ──┐
DA1 Todo API (GĐ3) ──┤ cùng một repo, nâng cấp dần
DA2 + Postgres/Prisma (GĐ4) ──┤
DA3 Mini SaaS API (GĐ5–GĐ17) ──┤ ← portfolio chính
DA4 Capstone DocuChat (GĐ18–GĐ22)──┘ ← sản phẩm hoàn chỉnh| # | Tên | Giai đoạn | Nền tảng | Sản phẩm giao |
|---|---|---|---|---|
| DA0 | Skeleton TypeScript | GĐ1 | — | Repo build & chạy được, lint + typecheck sạch |
| DA1 | Todo/Notes API | GĐ3 | DA0 | REST API + JWT + validation, deploy công khai |
| DA2 | Todo API trên Postgres | GĐ4 | DA1 | Prisma + migration + chứng minh fix N+1 |
| DA3 | Mini SaaS API | GĐ5–GĐ17 | DA2 | Portfolio chính: NestJS, auth, upload, jobs, search, test, Docker, CI, K8s |
| DA4 | Capstone "DocuChat" | GĐ18–GĐ22 | DA3 | AI SaaS chạy thật: RAG + streaming + billing + quota + observability |
DA0 — Skeleton TypeScript · GĐ1
Mục tiêu. Dựng toolchain từ số 0, không copy template.
Giao được khi: npm run dev (watch), npm run build && npm start (bản build thật sự chạy), npm run lint, npm run typecheck — cả bốn đều sạch. Env validate bằng zod, fail-fast lúc boot. Debug VSCode dừng đúng dòng trong file .ts.
Cạm bẫy dễ vấp: alias chạy dev nhưng vỡ khi build → GĐ1 mục 4.
DA1 — Todo/Notes API · GĐ3
Mục tiêu. REST API hoàn chỉnh bằng Express, deploy lên internet.
Phạm vi: auth JWT (register/login), CRUD todos scoped theo user, pagination, validate bằng Zod → 422, error handler tập trung, deploy Railway/Render.
Điều quan trọng nhất phải làm đúng: thứ tự middleware trong app.ts — error handler luôn cuối cùng.
Spec đầy đủ: GĐ3 — Dự án 1.
DA2 — Todo API trên Postgres · GĐ4
Mục tiêu. Thay mảng in-memory bằng database thật.
Phạm vi: Prisma schema quan hệ 1-n, migration versioned commit vào git, seed idempotent, Redis cache-aside (tuỳ chọn), full-text search bằng tsvector → GĐ4 mục 14.
Sản phẩm giao đặc trưng: một commit "before/after" chứng minh bằng số rằng N+1 đã được sửa (đếm số query trước và sau). Đây là bằng chứng bạn hiểu ORM, không chỉ dùng ORM.
DA3 — Mini SaaS API · GĐ5 → GĐ17 ← portfolio chính
Dự án lớn nhất. Mười một giai đoạn cùng đổ vào đây, mỗi giai đoạn thêm một lớp.
Lớp 0 — NoSQL đúng chỗ (GĐ5)
Thêm một collection Mongo (activity_events) bên cạnh Postgres, có $jsonSchema validator, compound index theo quy tắc ESR, TTL index, một aggregation pipeline có số liệu explain trước/sau. README phải trả lời được: vì sao cái này ở Mongo mà orders ở Postgres.
Lớp 1 — Nền NestJS (GĐ6)
Auth đầy đủ (argon2, JWT access 15m + refresh 7d có rotation, refresh lưu hash, logout = revoke), RBAC bằng guard, OAuth Google/GitHub, ValidationPipe global, exception filter, interceptor, Prisma + repository, Swagger /docs, unit + e2e test.
Lớp 2 — Kiến trúc (GĐ7)
Refactor một module có nghiệp vụ thật sang domain/application/infrastructure, có lint rule chặn vi phạm quy tắc phụ thuộc, test domain chạy không cần DB. Giữ các module khác ở kiểu Nest thường và so sánh hai kiểu trong README.
Lớp 3 — API thực chiến (GĐ8)
Rate limit qua Redis, WebSocket, cache-aside, versioning API, cursor pagination có sort ghép + tie-breaker, idempotency key, webhook verify HMAC, structured logging + request id, health check, graceful shutdown.
Lớp 4 — Xử lý nền (GĐ9)
Worker process riêng, ba queue theo mức ưu tiên, outbox + relay SKIP LOCKED, handler idempotent có test, retry backoff + jitter, DLQ + trang quản trị, cronjob có tz tường minh, job rollup theo con trỏ, metric queue depth.
Lớp 5 — Search (GĐ10)
Meilisearch/Typesense/ES trong compose, đồng bộ qua outbox, filter tenant_id bắt buộc, facet, phân trang search_after, job đối soát + metric drift, số đo zero-result rate trước/sau.
Lớp 6 — File & Email (GĐ11)
Presigned URL upload 3 bước, validate magic bytes, xử lý ảnh trong worker, xoá mềm + job purge, email qua outbox có idempotency key, SPF/DKIM/DMARC thật, suppression list từ webhook bounce.
Lớp 7 — Testing (GĐ12)
Testcontainers, factory, ma trận authorization, test tenant isolation, test transaction rollback, MSW mock dịch vụ ngoài (có cả đường lỗi), k6 load test, CI gate.
Lớp 8 — Vòng đời dữ liệu (GĐ13)
Audit log bất biến, TIMESTAMPTZ toàn bộ + xử lý múi giờ đúng, tiền bằng số nguyên minor unit, CHECK constraint, retention + ẩn danh hoá, backup + restore drill thật, migration expand-contract không downtime.
Lớp 9 — DevOps (GĐ14)
Dockerfile multi-stage, docker-compose (app + worker + Postgres + Redis + search) chạy một lệnh, GitHub Actions CI/CD, deploy Railway/VPS/ECS, HTTPS, zero-downtime.
Lớp 10 — Kubernetes (GĐ15)
Cluster local kind/k3d, manifest đủ ba probe (liveness không chạm DB), resources + --max-old-space-size, preStop + grace period khớp job dài nhất, HPA + PDB, rollout dưới tải k6 với 0 request lỗi.
Lớp 11 — Ranh giới (GĐ16–GĐ17)
Kiểm toán timeout + idempotency, phân loại C-vs-L từng endpoint, distributed tracing xuyên queue, biến DA3 thành modular monolith có lint chặn import xuyên biên, tách đúng một service qua queue, contract test, và số đo trước/sau về chi phí của lần tách đó.
Spec gốc: GĐ6 — Dự án 3.
Tự chấm — DA3 đạt khi bạn trả lời được, có chỉ vào code:
- "Nếu refresh token của tôi bị đánh cắp thì sao?" → rotation phát hiện được, chỉ ra chỗ code.
- "User A có đọc được dữ liệu user B không?" → chỉ vào test chứng minh không, không phải chỉ vào câu
where. - "Webhook provider gửi lại lần hai thì sao?" → idempotency, chỉ vào test.
- "Worker chết giữa lúc chạy 10.000 job thì sao?" → chỉ vào lần thử nghiệm thật, không mất, không trùng.
- "Search index lệch với DB thì bao lâu bạn biết?" → chỉ vào metric drift và job đối soát.
- "Deploy lúc đang có traffic thì sao?" → graceful shutdown, có kết quả k6 chứng minh 0 lỗi.
- "Mất database lúc này thì bao lâu khôi phục?" → nói được con số RTO đo được từ lần diễn tập.
- "Vì sao anh không tách microservice?" → chỉ vào 4 phép thử ranh giới đã áp dụng và số đo chi phí.
DA4 — Capstone "DocuChat" · GĐ18 → GĐ22
Mục tiêu. AI SaaS chạy thật, có người trả tiền được.
Phạm vi: auth + org (tenant_id), Stripe Free/Pro + webhook, upload → ingestion pipeline (extract → chunk → embed → pgvector theo tenant), chat RAG streaming SSE có trích dẫn + nút Stop, quota + rate limit theo gói, cost tracking + dashboard + model routing, moderation + chống prompt injection + redact PII, Langfuse + Sentry + alert, deploy đầy đủ.
Lớp nền — GĐ18 (System Design): rà stateless, chạy 3 instance sau LB, PgBouncer, timeout + retry có jitter + circuit breaker cho LLM, load shedding, dashboard RED, và chạy k6 tới điểm gãy để tìm nút thắt thật.
Spec đầy đủ: GĐ22 — Capstone.
Thứ tự phụ thuộc — cái gì phải xong trước
GĐ1 ─→ GĐ2 ─→ GĐ3(DA1) ─→ GĐ4(DA2) ─→ GĐ6(DA3 lớp 1) ─→ GĐ8 ─→ GĐ9 ─→ GĐ14
│ │ │ │
│ ▼ ▼ │
│ GĐ5 GĐ7 GĐ15 ─→ GĐ16 ─→ GĐ17
│ │
│ GĐ10 (sau GĐ9) │
│ ▼
│ GĐ11, GĐ12, GĐ13 (song song được) GĐ18
│ │ │
│ └──→ GĐ19 ─→ GĐ20 ─→ GĐ21 ─→ GĐ22 ◄────┘Ghi chú về thứ tự:
- GĐ11 / GĐ12 / GĐ13 học song song được — không phụ thuộc nhau. Nếu phải chọn một: GĐ12 (testing) trước, vì nó làm mọi giai đoạn sau an toàn hơn.
- GĐ10 (search) phải sau GĐ9 — đồng bộ index dùng lại outbox + worker.
- GĐ16 → GĐ17 → GĐ18 là một khối liền mạch. Học GĐ17 mà chưa có GĐ16 thì chỉ là sơ đồ hộp và mũi tên.
- GĐ18 học sau GĐ22 cũng được nếu bạn muốn có hệ thống thật mà mổ xẻ. Nhưng nếu đang chuẩn bị phỏng vấn, đọc khối GĐ16–18 sớm hơn — nó độc lập tương đối.
- GĐ19 có thể bắt đầu song song với GĐ11–13 nếu bạn nóng lòng chạm vào AI.
Theo dõi tiến độ
Đánh dấu khi giao được sản phẩm, không phải khi đọc xong tài liệu.
- [ ] DA0 — build + start chạy thật, lint/typecheck sạch
- [ ] DA1 — Todo API deploy công khai, có URL truy cập được
- [ ] DA2 — Postgres + migration, có bằng chứng số liệu fix N+1
- [ ] DA3 lớp 0 — collection Mongo có validator + index + số liệu explain
- [ ] DA3 lớp 1 — auth đầy đủ + RBAC + OAuth + Swagger
- [ ] DA3 lớp 2 — một module Clean Architecture + lint chặn vi phạm + so sánh trong README
- [ ] DA3 lớp 3 — rate limit + cache + cursor pagination + idempotency + observability
- [ ] DA3 lớp 4 — worker riêng + outbox + DLQ + cron; đã giết worker giữa 10k job và chứng minh không mất/không trùng
- [ ] DA3 lớp 5 — search chạy thật + job đối soát + số đo zero-result rate
- [ ] DA3 lớp 6 — upload presigned + email có SPF/DKIM/DMARC pass
- [ ] DA3 lớp 7 — Testcontainers + ma trận authz + k6 + CI gate
- [ ] DA3 lớp 8 — audit log + timezone + tiền + backup đã diễn tập restore
- [ ] DA3 lớp 9 — Docker + CI/CD + deploy
- [ ] DA3 lớp 10 — K8s local, rollout dưới tải k6 với 0 lỗi
- [ ] DA3 lớp 11 — modular monolith có lint + tách 1 service + contract test + số đo chi phí
- [ ] DA4 — DocuChat chạy production, có người dùng thật (dù chỉ 5 người)
- [ ] DA4 + GĐ18 — chạy 3 instance, chịu được k6 tới điểm gãy đã biết
Vài lời về portfolio
Một dự án sâu > năm dự án nông. DA3 với lịch sử git 6 tháng, có test, có CI, có ADR ghi lại quyết định — mạnh hơn năm repo tutorial gộp lại.
README là thứ được đọc, không phải code. Trong README của DA3/DA4 phải có: kiến trúc (một sơ đồ), các quyết định và lý do (vì sao Postgres cho đơn hàng nhưng Mongo cho event log, vì sao modular monolith không phải microservice, vì sao Meilisearch không phải Elasticsearch), cách chạy local trong một lệnh, và những gì bạn cố ý chưa làm kèm lý do. Mục cuối cùng đó gây ấn tượng mạnh nhất — nó cho thấy bạn phân biệt được "chưa làm" và "không biết".
Giữ lại các commit "before/after". Commit fix N+1, commit thêm index kèm số liệu EXPLAIN, commit sửa lỗ authz do test bắt được, commit đo chi phí trước/sau khi tách service. Đó là bằng chứng về quá trình tư duy — thứ mà một repo hoàn hảo không có lịch sử không thể chứng minh.
