Hướng dẫn học kỹ thuật qua dự án KCDS (Khí Công Dưỡng Sinh)¶
Bộ tài liệu này trích xuất các kỹ thuật được dùng thực tế trong dự án Discord Bot KCDS — một hệ thống phát nhạc + quản lý lịch tập qua web panel. Mục tiêu: giúp newbie học đủ lý thuyết và thấy code thực tế ngay trong project, sau đó áp dụng vào project khác.
Bản đồ học tập¶
Bắt đầu từ đây
│
▼
┌─────────────────────────────┐
│ Bài 0: Nguyên lý nền tảng │ ← Vocabulary: SOLID, SoC, DRY, YAGNI, IoC
└──────────────┬──────────────┘
▼
┌─────────────────────────────┐
│ Bài 1: Khởi tạo project │ ← Setup: venv, cấu trúc thư mục, config.py,
│ │ Pydantic input types, uvicorn
└──────────────┬──────────────┘
▼
┌─────────────────────────────┐
│ Bài 2: FastAPI + ASGI │ ← Nền tảng: async Python + HTTP
│ Bài 3: Dependency Injection│ ← Pattern cốt lõi của FastAPI (IoC)
└──────────────┬──────────────┘
│
▼
┌─────────────────────────────┐
│ Bài 4: SQLAlchemy Core │ ← Tương tác DB không ORM
│ Bài 5: Layer Architecture │ ← SoC + DIP hiện thực cụ thể
└──────────────┬──────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ Bài 6: Vite + Vanilla JS │ Bài 7: discord.py │ ← Học song song
│ (Frontend) │ (Bot async) │
└─────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────┐
│ Bài 8: Auth OAuth + JWT │ ← Bảo mật
└──────────────┬──────────────┘
▼
┌─────────────────────────────┐
│ Bài 9: Testing │ ← DI + mock — test qua interface
│ Bài 10: Trade-off & Anti-pt│ ← Khi nào KHÔNG áp dụng pattern đã học
└─────────────────────────────┘
Danh sách bài¶
| Bài | Chủ đề | File project reference | Độ khó |
|---|---|---|---|
| 00 | Nguyên lý nền tảng (SOLID, SoC, DRY, IoC, Fail-Fast) | không có — vocabulary | ⭐ |
| 01 | Khởi tạo project FastAPI từ zero | admin/src/config.py, .env.example |
⭐ |
| 02 | FastAPI + ASGI + async/await | admin/src/main.py, routers/audio.py |
⭐⭐ |
| 03 | Dependency Injection trong FastAPI | admin/src/auth.py, deps.py |
⭐⭐⭐ |
| 04 | SQLAlchemy Core + aiosqlite | admin/src/db/engine.py, repositories/audio_repo.py |
⭐⭐⭐ |
| 05 | Kiến trúc 3 lớp Router→Service→Repo | repositories/base.py, services/audio_service.py |
⭐⭐⭐⭐ |
| 06 | Vite + Vanilla JS | admin/src/ui/src/ |
⭐⭐ |
| 07 | discord.py + Async patterns | bot/src/scheduler.py, notifier.py |
⭐⭐⭐⭐ |
| 08 | Google OAuth2 + JWT + HMAC | admin/src/auth.py, services/signed_url.py |
⭐⭐⭐⭐⭐ |
| 09 | Testing (unit / integration / E2E) | project chưa có test thật — bài dùng ví dụ tương đương trên entity của project | ⭐⭐⭐ |
| 10 | Trade-off & Anti-patterns | tổng hợp từ bài 2-8 | ⭐⭐⭐⭐⭐ |
Lộ trình đề xuất: Đọc bài 0 (nguyên lý) → bài 1 (setup thực hành) → bài 2-8 tuần tự → bài 9 (khi muốn viết test) → bài 10 (khi đã quen pattern để phản biện).
Stack đầy đủ của dự án¶
┌─────────────────────────────────────────────────────────────────┐
│ CLIENT (Browser) │
│ Vite + Vanilla JS (ES modules, fetch API, cookie auth) │
└─────────────────────────────┬───────────────────────────────────┘
│ HTTPS (Cloudflare Tunnel)
┌─────────────────────────────▼───────────────────────────────────┐
│ ADMIN API (Python) │
│ FastAPI + Uvicorn (ASGI) │
│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ Router │→ │ Service │→ │ Repository │ │
│ │ (HTTP) │ │(Business)│ │ (SQLite/File)│ │
│ └──────────┘ └──────────┘ └──────────────┘ │
│ Auth: Google OAuth2 → JWT → HttpOnly Cookie │
└─────────────────────────────┬───────────────────────────────────┘
│ Shared Volume
┌─────────────────────────────▼───────────────────────────────────┐
│ SQLITE + FILESYSTEM │
│ khi_cong.db (metadata) /data/audio/ (MP3 files) │
└─────────────────────────────┬───────────────────────────────────┘
│ Read/Write
┌─────────────────────────────▼───────────────────────────────────┐
│ BOT (Python) │
│ discord.py + asyncio │
│ Scheduler loop 2s → drain commands → check schedule │
│ FFmpeg PCM → Discord Voice │
└─────────────────────────────────────────────────────────────────┘
Các pattern quan trọng nhất — Quick Reference¶
1. Async/await cơ bản¶
# Hàm async — dùng await bên trong
async def get_user(session, email: str) -> dict | None:
result = await session.execute( # await I/O
select(users).where(users.c.email == email)
)
return dict(result.fetchone()._mapping) if result.fetchone() else None
2. Dependency Injection chain¶
# session → user → role check → router
async def get_session(): yield session
def get_user_repo(): return SqliteUserRepository()
async def get_current_user(session=Depends(get_session), repo=Depends(get_user_repo)): ...
def require_editor(user=Depends(get_current_user)): ...
@router.post("/resource", dependencies=[Depends(require_editor)])
async def create_resource(session=Depends(get_session)): ...
3. Transaction order (file + DB)¶
# Upload: file TRƯỚC, DB SAU, rollback file nếu DB lỗi
dest.write_bytes(data)
try:
await repo.create(session, ...)
except:
dest.unlink() # rollback
raise
# Delete: DB TRƯỚC, file SAU (lỗi file chỉ log)
await repo.delete(session, id)
try:
dest.unlink()
except OSError as e:
logger.warning(e) # không raise
4. Abstract interface (DIP)¶
class MyRepository(ABC):
@abstractmethod
async def create(self, session, *, name: str) -> dict: ...
class SqliteMyRepository(MyRepository):
async def create(self, session, *, name: str) -> dict:
# SQLite implementation
class MyService:
def __init__(self, repo: MyRepository): # interface, không phải implementation
self.repo = repo
5. Async mutex¶
# Gán SYNC trước await — không có race condition
self._current_id = entry["id"] # synchronous
task = asyncio.create_task(...) # background — không block
6. HMAC signing¶
import hmac, hashlib, time
def sign(resource_id: int, secret: str, ttl: int = 1800) -> tuple[str, int]:
expires = int(time.time()) + ttl
sig = hmac.new(secret.encode(), f"{resource_id}:{expires}".encode(), hashlib.sha256).hexdigest()
return sig, expires
def verify(resource_id: int, sig: str, expires: int, secret: str) -> bool:
if expires < time.time(): return False
expected = hmac.new(secret.encode(), f"{resource_id}:{expires}".encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(sig, expected)
Prerequisites — Cần biết trước¶
- Python cơ bản (functions, classes, exceptions)
- HTTP basics (GET/POST, headers, status codes)
- SQL cơ bản (SELECT, INSERT, UPDATE, DELETE, JOIN)
- JavaScript cơ bản (functions, async/await, fetch)
Không cần biết trước: React, Vue, Django, SQLAlchemy ORM, Docker.
Nguyên lý xuyên suốt bộ guide¶
Các nguyên lý ở bài 0 được minh họa lặp lại trong các bài còn lại. Bảng dưới giúp bạn thấy pattern nào map về nguyên lý nào — đây là chìa khóa để chuyển giao kiến thức sang project khác:
| Nguyên lý | Bài minh họa chính | Hiện thực cụ thể |
|---|---|---|
| Separation of Concerns | 2, 5, 6 | Router/Service/Repo; api.js/state/render |
| Dependency Inversion | 3, 5, 9 | Service nhận AudioRepository ABC qua constructor |
| Inversion of Control | 3, 7 | Depends(), @client.event, @router.get() |
| Single Responsibility | 5 | Mỗi layer 1 lý do thay đổi |
| Fail Fast | 1, 2, 8 | config.py validate() + lifespan startup + JWT_SECRET check |
| Fail Soft (có điều kiện) | 7 | _TICK_RECOVERABLE + notifier fire-and-forget |
| DRY có kỷ luật | 3, 6 | deps.py, api.js centralized |
| YAGNI | 4, 6 | Không ORM, không React khi chưa cần |
| Explicit > Implicit | 4 | SQLAlchemy Core thay ORM |
| POLS | 2, 7 | Response wrap {data}, _current_entry_id tên rõ intent |
| Defense in Depth | 6, 8 | esc() + CSP + HttpOnly cookie + RBAC |
| Least Privilege | 8 | JWT TTL ngắn, role fetch DB mỗi request |
Cách đọc bảng này: Khi gặp kỹ thuật mới ở project khác, hỏi "kỹ thuật này hiện thực nguyên lý nào?". Nếu không biết → đọc lại bài 0. Nếu biết → bạn đã chuyển giao thành công.
Cách học hiệu quả¶
- Đọc bài 0 trước → có vocabulary nguyên lý, rồi vào bài 1-8 mới hiểu sâu
- Đọc bài lý thuyết → hiểu "tại sao"
- Tìm đoạn code tương ứng trong project → xem cách dùng thực tế
- Đọc section "Nguyên lý tổng quát" cuối mỗi bài → abstract pattern lên principle chuyển giao được
- Làm bài tập cuối mỗi bài → viết tay, không copy
- Trả lời câu hỏi tự kiểm tra → nếu không trả lời được, đọc lại
- Bài 10 sau cùng → học phản biện pattern, không học máy móc
- Áp dụng vào project cá nhân nhỏ → cách học nhanh nhất
Đối với newbie thật sự¶
Nếu bạn mới học Python web, đừng cố đọc hết 10 bài trong 1 tuần. Gợi ý:
- Tuần 1: Bài 0 (nguyên lý) + bài 1 (setup + làm bài tập Notes API)
- Tuần 2-3: Bài 2 + 3 (async + DI)
- Tuần 4-5: Bài 4 + 5 (DB + layer architecture)
- Tuần 6-7: Bài 6 hoặc 7 (chọn 1 — frontend hoặc bot)
- Tuần 8: Bài 8 (auth — khó, cần nền tảng vững)
- Tuần 9+: Bài 9 (test) + bài 10 (trade-off) — khi đã thực hành tự xây app
Project thực hành gợi ý¶
Sau khi học xong bộ tài liệu này, thử tự xây:
Cấp độ 1 — CRUD API đơn giản:
- FastAPI + SQLite + SQLAlchemy Core
- 1 entity (ví dụ: Task trong todo app)
- CRUD đầy đủ + layer architecture
Cấp độ 2 — Thêm auth: - JWT với username/password (chưa cần OAuth) - 2 role: admin, user - Protected endpoints
Cấp độ 3 — Full stack: - Thêm Vite + Vanilla JS frontend - Build và serve qua FastAPI StaticFiles - Upload file (ví dụ: avatar)
Cấp độ 4 — Bot integration: - Discord bot với scheduled notifications - Đọc data từ SQLite (share với FastAPI) - Manual control qua command queue pattern