SDD — System Design Document
Thiết kế hệ thống cho
tokyo.conan.school. Trả lời "làm thế nào" cho các yêu cầu trong Requirements. Trạng thái: v1.0 · Nguyên tắc nền tảng: Cloudflare-only.
1. Tổng quan kiến trúc
Tokyo là một ứng dụng React Router (framework mode) phục vụ qua Cloudflare Pages, gọi backend api.conan.school (Cloudflare Workers) qua same-origin /api proxy để giữ cookie phiên member. Dữ liệu sống nằm hoàn toàn trên Cloudflare D1.
Ánh xạ triển khai: thư mục com.conan.school/ → Cloudflare Pages project conan-com → phục vụ cả com.conan.school và tokyo.conan.school. Proxy /api cùng origin chuyển tiếp cookie member tới api.conan.school.
2. Thành phần
| Thành phần | Công nghệ | Vai trò |
|---|---|---|
| Tokyo UI | React Router 7, Vite | Render trải nghiệm Builder; route theo app/routes/* |
| Hosting | Cloudflare Pages (conan-com) | Phục vụ static + SSR/edge |
| API | Cloudflare Workers (api.conan.school) | Auth, dữ liệu khóa học, membership |
| Database | Cloudflare D1 | Nguồn dữ liệu sống duy nhất |
| Workflow | Cloudflare Workflows | Đối soát chuyển khoản → kích hoạt paid |
| AI phụ | Workers AI (@cf/llava-1.5-7b-hf) | OCR biên lai best-effort (không phải nguồn sự thật) |
3. Mô hình dữ liệu — Course Information
Mô hình khóa học là additive: bảng mới đặt cạnh courses hiện có, không phá pipeline curriculum/quiz cũ.
Cấu trúc phân cấp
Course → Unit → Learning Experience → ActivityChạy song song: Concepts · Capabilities · Artifacts · Assessments · Capstone.
Thực thể chính
| Thực thể | Khóa/quan hệ | Trường tiêu biểu |
|---|---|---|
| Course | gốc | id, slug, title, subtitle, description, status, version; clarity_question, core_problem, promise, course_outcome; clarity_area, course_kind, difficulty, target_audience |
| Concept (course-owned) | course_id | name, definition, description, sort_order — không dùng chung |
| Capability (course-owned) | course_id | name, description, target_level, sort_order |
| Unit | course_id | unit_number, title, subtitle, unit_question, description, unit_outcome |
| Learning Experience | unit_id | title, description, experience_type, expected_output |
| Activity | trong LE | bước nhỏ (MVP: tùy chọn) |
| Artifact | course_id/unit_id | name, description, artifact_type |
| Capstone | course_id | title, description, outputs[] |
| Assessment/Rubric | course_id | tiêu chí có trọng số (course_rubric_criteria) |
| Reflection | Unit | reflection_prompt |
Enum quan trọng:
clarity_area∈LEARN_THINK · UNDERSTAND · CHOOSE_CREATE · COMMUNICATE_SELL · BUILDcourse_kind∈FOUNDATION · CORE · APPLIED · STUDIOexperience_type∈CASE · INQUIRY · READING · DISCUSSION · INTERVIEW · OBSERVATION · SIMULATION · WRITING · BUILD · CRITIQUE · REFLECTION · ASSESSMENTassessment∈DIAGNOSTIC · FORMATIVE · SUMMATIVE · CAPSTONE · PEER_REVIEW · SELF_REFLECTION
Quy tắc cứng: Concepts thuộc đúng một Course. Không có bảng concept global many-to-many. Nhờ vậy mỗi khóa tự chứa ngữ nghĩa, sửa/xóa không ảnh hưởng khóa khác.
Lưu ý D1 legacy: client D1 của
api.conan.schooltiền tố nhiều bảng bằnglegacy_. Tuy nhiên nhóm Course Information dùng bảng không tiền tố (courses,course_concepts,course_enrollments,users) — hai họ bảng cùng tồn tại; migration raw-SQL phải nhắm đúng họ bảng.
4. Membership & Trial — thiết kế
Suy diễn nhóm quyền (tại request time)
isPaidTier(tier) = tier != null && lower(trim(tier)) != 'free'
group =
paid nếu isPaidTier(membership_tier)
trial nếu (không paid) và now < trial_expires_at
free nếu (không paid) và (trial thiếu hoặc hết hạn)Nguồn: users.membership_tier, users.trial_started_at, users.trial_expires_at. Trả cho frontend qua /api/user/me: profile.entitlement_group, trial_expires_at, trial_days_left.
Lazy-init trial
Ở request xác thực đầu tiên khi chưa paid và trial_started_at IS NULL: trial_started_at = now, trial_expires_at = now + 7 ngày. Idempotent — thao tác chỉ đặt khi còn NULL.
Kích hoạt trả phí (Workflow — phone match)
OCR biên lai (
@cf/llava-1.5-7b-hf) chỉ là bằng chứng phụ. Model@cf/meta/llama-3.2-11b-vision-instructbị license-gate (5016) nên không nằm trên đường kích hoạt tự động.
5. Xác thực & phiên
- Đăng nhập qua
/login→/auth/callback; đăng xuất/logout. - Cookie phiên HttpOnly + Secure, gửi kèm qua same-origin
/api. - Phân quyền thực thi ở backend theo
entitlement_group; UI chỉ ẩn/hiện.
6. Cấu trúc route (frontend)
Route nằm ở com.conan.school/app/routes/* (React Router). Nhóm chính: học tập (courses, my-courses, my-journey, concepts), thực hành (challenges, battles, workshops, programs, classes), sản phẩm (portfolio, *-brands, brand-model), cộng đồng (events, online-events, insiders, feedback, calendars, affiliate), tiến bộ (score, badges, member-intelligence, membership, profile, portrait). Bản đồ đầy đủ: Bản đồ tính năng.
7. Deploy & CI/CD
- Trigger: push
main→ GitHub Actions (path-filter theo thư mục app). - Cơ chế: workflow per-app gọi reusable
_deploy-cloudflare.yml, chạywrangler pages deployvới Cloudflare API token. - Docs site này: app_dir
docs.conan.school, buildnpm run build(VitePress), output.vitepress/dist, Pages projectdocs-conan-school→docs.conan.school.
8. Ràng buộc & invariant
- Cloudflare-only: không thêm phụ thuộc Supabase mới (có kiểm tra tự động
check:cloudflare-only). - Same-origin API: UI không gọi thẳng
api.conan.schoolcross-origin cho hành động cần cookie. - Course Information additive: không sửa phá pipeline curriculum/quiz cũ.
- Concepts course-owned: tuyệt đối không global.