Bỏ qua

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ả

  1. Đọ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
  2. Đọc bài lý thuyết → hiểu "tại sao"
  3. Tìm đoạn code tương ứng trong project → xem cách dùng thực tế
  4. Đọc section "Nguyên lý tổng quát" cuối mỗi bài → abstract pattern lên principle chuyển giao được
  5. Làm bài tập cuối mỗi bài → viết tay, không copy
  6. Trả lời câu hỏi tự kiểm tra → nếu không trả lời được, đọc lại
  7. Bài 10 sau cùng → học phản biện pattern, không học máy móc
  8. Á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