Skip to content

SDD-001 — Platform Architecture

Nguyên tắc: One World, One Learner, One Platform. Nemo12 là distributed education platform về business capability, unified learner system về technology.

1. High-Level

text
nemo12.com ── Cloudflare Edge (WAF, DDoS, rate limit, bot)

   ├─ apps (Vite/React SPA + assets-on-Worker): web, learn, marlins, mentors, admin, docs(VitePress)

   └─ api.nemo12.com ── Hono on Workers (modular monolith)
         ├─ D1  `nemo12-platform` (id f4275a70-66fa-4746-8292-40f96a7ed288) — system of record
         ├─ R2  `nemo12-content` — heavy assets/media
         ├─ KV  `nemo12-config` (id f08c239409404bfe82b5aad303de362d) — config/flags/manifests cache
         ├─ Queues `nemo12-events` (+ `nemo12-events-dlq`) — async fan-out
         ├─ Workflows — durable multi-step processes
         └─ AI Gateway `nemo12` — mọi AI request (bắt buộc, không bypass)

Tài nguyên trên account Cloudflare ff302e77218f9616c3baaf01db59d64e, zone nemo12.com (id 09caf325ee80eca4743957e03b655867). Tạo ngày 2026-08-13.

2. Modular Monolith (REQ-PLT-02)

Bounded modules trong một Worker api (tách service chỉ khi có lý do scale/security rõ):

text
identity · family · learner · school · enrollment · learning · assessment
evidence · recommendation · mentor · community/interaction · content · media
ai · notification · admissions

Mỗi module: thư mục riêng trong workers/api/src/modules/, export router Hono + event handlers; không import chéo internals (chỉ qua public interface của module).

3. Cloudflare Services (REQ-PLT-01)

Nhu cầuDịch vụQuy tắc
ComputeWorkersHono, TypeScript
RelationalD1Một database nemo12-platform; school phân biệt bằng school_id/program_id, không tách DB theo school (ADR-002)
Heavy contentR2metadata ở D1, D1 chỉ giữ content_ref (SDD-004, SDD-009)
Cache/configKVkhông chứa learner state quan trọng
AsyncQueuesbatching, retry, DLQ (SDD-006 §5)
Durable processWorkflowsdiagnosis, evaluation, recalculation…
AIAI Gateway nemo12logging, caching, rate-limit; cấm direct provider call và cấm "probe-then-fallback" kiểu chuyenchon (RISK-022)
API edgeAPI Shield/Gateway (khi bật) + code-level hardeningmethod/path allowlist, body cap (kế thừa pattern sutucon shared/gateway.ts)

4. Frontend (REQ-PLT-05, REQ-BRD-01, REQ-SCH-01)

text
apps/
├── web/       nemo12.com (+ www)   — public, 6 school pages /turtle…/whale, SEO+OG
├── learn/     learn.nemo12.com     — Nemos; school = route context (/turtle, /shark…)
├── marlins/   marlins.nemo12.com   — parents
├── mentors/   mentors.nemo12.com   — Dolphins (build sau, thứ 4)
├── admin/     admin.nemo12.com     — internal, sau Cloudflare Access (build cuối)
└── docs/      docs.nemo12.com      — VitePress, render docs/ canonical (sau Access)
  • Vite + React + TypeScript, Cloudflare Vite plugin; deploy dạng Worker với static assets (không dùng Pages — hợp nhất một mô hình deploy).
  • School không có app riêng — chỉ là route/theme context (REQ-SCH-01).
  • @latest khi cài, lock trong lockfile (Vite 8.x, Tailwind v4, shadcn CLI mới nhất).

5. API (REQ-PLT-04, REQ-PLT-10)

  • Base: https://api.nemo12.com/v1/ — resources: /learners /families /invitations /enrollments /evidence /assessments /recommendations /learning /content /media /interactions /notifications.
  • Contract OpenAPI sinh từ code (@hono/zod-openapi): mọi route bắt buộc có zod schema (fix RISK-024 — sutucon hand-maintained openapi). GET /v1/openapi.json public.
  • API-first cho mobile: không logic nào chỉ nằm trong web app; response envelope {data} / {error:{code,message}} theo error taxonomy SDD-006 §11.
  • Versioning: /v1 ổn định; breaking change → /v2 song song (REQ-NFR-10).

6. Identity, Auth & Access (REQ-ACC-*, REQ-SEC-05)

Parent-first (Q-006):

text
Parent bấm "Đăng nhập với Google" (Google Identity Services, id_token)
→ api verify id_token (aud = GOOGLE_CLIENT_ID, email_verified)
→ chưa có user? tạo `users` + `families` + membership 'owner' (tự động, không form)
→ session: token opaque 32B, lưu sha256(token), cookie `nemo12_session`
  Domain=.nemo12.com HttpOnly Secure SameSite=Lax, Max-Age 30d, CÓ rotation khi refresh
  • GSI id_token flow → không cần Google client secret (kế thừa sutucon, RISK-021 tránh được). GOOGLE_CLIENT_ID là var công khai.
  • Invite flow: parent tạo invitation (email hoặc link + mã) → learner login Google qua link → account learner tạo + gắn family_id + guardian links. Learner không qua invite = không enroll được (REQ-ACC-03).
  • Mobile (REQ-ACC-06): cùng endpoint đổi id_token → session token, client mobile giữ token trong secure storage, gửi qua Authorization: Bearer — cookie và bearer song song.
  • RBAC: bảng role_assignments (user_id, role, scope) — roles learner|guardian|mentor|staff|admin; mọi authorization ở backend, theo quan hệ family/assignment (REQ-SEC-02).
  • Cloudflare Access bảo vệ admin. + docs. (+ staging). Cấu hình Zero Trust: app per hostname, policy = email domain/list; audience + team domain đặt qua env var, cấm hardcode (REQ-SEC-06, fix RISK-013). Trạng thái 2026-08-13: chưa bật được qua API token hiện có — cần làm trên dashboard; admin/docs không deploy public cho tới khi Access bật.

7. Data Layer (REQ-ACC-04, REQ-PLT-11)

  • Canonical identity: users.id (UUIDv7), learners.id riêng (một user có thể là learner; guardian liên kết qua guardians/family_members). Email chỉ là thuộc tính đăng nhập — cấm làm khóa dữ liệu (fix RISK-001 owner_key=email của chuyenchon).
  • Core tables (migration 0001): users, families, family_members, learners, guardians, invitations, sessions, auth_identities, role_assignments, audit_log.
  • Domain tables theo SDD-002/003/004/005/009.
  • Migrations: một sequence duy nhất migrations/ ở root monorepo, áp bằng wrangler d1 migrations apply nemo12-platform; idempotent khi có thể; cấm DML destructive trộn DDL; cấm nhiều thư mục migration trỏ cùng DB (fix RISK-004/005 của cả 2 legacy).
  • Timestamps: TEXT ISO-8601 UTC (SQLite), tên cột *_at.

8. Events & AI plumbing (REQ-PLT-03, REQ-PLT-09)

  • Domain events chuẩn: {event_id, idempotency_key, type, occurred_at, producer, version, payload} — publish sau commit, qua nemo12-events; consumer idempotent; quá retry → DLQ.
  • Catalog khởi điểm: UserCreated, FamilyCreated, LearnerInvited, LearnerJoined, EnrollmentCreated, EvidenceRecorded, AssessmentCompleted, SkillMasteryChanged, RecommendationGenerated, LearningExperienceCompleted, MentorInteractionRecorded, ParentObservationRecorded, CommentCreated, MediaUploaded, MediaModerated.
  • AI: một module ai/ duy nhất gọi AI Gateway nemo12; ghi {model, prompt_version, input_ref, output, confidence, evaluation_state} cho mọi output quan trọng (REQ-INT-14). Prompt registry versioned trong repo (packages/ai/prompts/). Provider mặc định: Workers AI (llama) cho tác vụ rẻ, có thể route Claude qua Gateway cho đánh giá chất lượng cao (Q-020 tự quyết — xem open-questions).

9. Monorepo (REQ-PLT-07)

text
nemo12/
├── apps/{web,learn,marlins,mentors,admin,docs}
├── workers/api            # Hono modular monolith
├── workers/consumers      # queue consumers (tách khi cần)
├── packages/{design-system,domain,database,events,ai,auth,shared}
├── migrations/            # một sequence duy nhất cho nemo12-platform
└── docs/                  # canonical documentation (REQ-PLT-08)

npm workspaces (Node 22). CI: GitHub Actions per-path (kế thừa mô hình 2 legacy repo — đã chạy ổn), deploy bằng wrangler deploy với CLOUDFLARE_API_TOKEN secret.

10. Documentation plane (REQ-PLT-08)

docs/ = canonical duy nhất; VitePress render tại apps/docs; frontmatter machine-readable (conventions.md); CI check broken links + trace closure (QG-001).

11. Configuration rules (REQ-SEC-06)

Cấm hardcode: domain names, school ids, owner emails, Access audiences, upstream URLs — tất cả qua vars/secrets trong wrangler config + packages/shared/config. (Fix RISK-013/016/023 — hai legacy hardcode domain ở 45+ và ~30 files.)

Trace

REQMục
REQ-PLT-01..05,07,08,10,11§1–§5, §7, §9, §10
REQ-ACC-01..06§6
REQ-BRD-01/02§4
REQ-SCH-01§4
REQ-SEC-05/06, REQ-MEN-01§6, §11