The repo layout
สิ่งที่จะสร้าง
หัวข้อที่มีชื่อว่า “สิ่งที่จะสร้าง”โครงสร้างระดับบนสุดของ FitTrack repository — directory ที่ถือของสามอย่างที่คุณจะสร้าง และ config ตัว shared ที่ผูกทั้งหมดเข้าด้วยกัน:
fittrack/├── api/ # The FastAPI backend (from the last lesson)├── mobile/ # The Flutter app (Modules 9–10)├── web/ # The Svelte web companion (Module 11)├── .env.example # Documented config every service reads├── .gitignore└── README.mdบทนี้ไม่ได้เขียนโค้ดเยอะ — แต่วาง ว่าอะไรไปอยู่ตรงไหน เพื่อให้ทุก module ถัดไปมีบ้านที่ชัดเจน api/ มีอยู่แล้วจาก The Python toolchain →; mobile/ กับ web/ สร้างเป็น directory เปล่าตอนนี้แล้วเติมทีหลัง artifact ตัวเดียวที่มีเนื้อจริง ๆ คือ .env.example ลิสต์ที่ documented ตัวเดียวของทุกค่า config ที่ backend และ client ต้องใช้
FitTrack เป็น monorepo โดยตั้งใจ: Git repository เดียวถือทั้ง backend และ client ทั้งสอง แทนที่จะเป็นสาม repo แยกกัน แก่นของโปรเจกต์ทั้งหมด — “สร้าง product เดียวสำหรับ client สองตัวบน backend ตัว shared เดียว” — คือคำประกาศเรื่อง contract ระหว่าง API กับ app ที่เรียกใช้ เมื่อ backend เพิ่ม field weight_kg หรือเปลี่ยนชื่อ endpoint, Flutter กับ Svelte client ต้องเปลี่ยนตามด้วย ใน repo เดียว นั่นคือ commit เดียวที่แตะทั้ง api/ กับ mobile/ ด้วยกัน review เป็นการเปลี่ยนแปลงเดียว และเป็นไปไม่ได้เลยที่ client จะอ้างถึงรูปร่าง API ที่ไม่มีอยู่บน commit เดียวกัน ถ้าแยกเป็นสาม repo การเปลี่ยนแปลงเดียวกันกลายเป็นสาม pull request กับการเต้นเรื่อง versioning เพื่อให้เข้ากันได้
แต่ละ top-level directory ถือครอง deployable ตัวเดียว และไม่มีอะไรรั่วข้ามกัน:
api/เป็นโปรเจกต์ Python ที่ self-contained — มีpyproject.tomlของตัวเอง, virtualenv ของตัวเอง ไม่มีอะไรในmobile/หรือweb/import จากapi/; ทั้งสองคุยกับ backend ผ่าน HTTP เท่านั้นmobile/เป็นโปรเจกต์ Flutter ที่ self-contained — มีpubspec.yamlของตัวเองweb/เป็นโปรเจกต์ SvelteKit ที่ self-contained — มีpackage.jsonของตัวเอง
isolation แบบนั้นแหละที่กัน monorepo ไม่ให้กลายเป็นก้อนพันกัน: repo จัดกลุ่มโปรเจกต์เข้าด้วยกัน แต่แต่ละตัวยัง build, test และ deploy ได้ด้วยตัวเอง เหมือนตอนอยู่ใน repo แยก
ส่วน shared คือ .env.example — template ที่ commit ไว้และไม่เป็นความลับ ลิสต์ทุก environment variable ที่ส่วนใด ๆ ของ FitTrack อ่าน พร้อมค่า placeholder ตัว .env จริง (ที่มี Supabase key ของจริง) ถูก git-ignore และไม่เคย commit; .env.example คือ documentation ว่าต้องตั้งค่า อะไร บ้าง contributor คนใหม่ copy ไฟล์นี้เป็น .env เติม Supabase credential ของตัวเอง แล้วทุก service ก็รู้ว่าต้องมองที่ไหน การเก็บลิสต์ canonical ตัวเดียวดีกว่าให้แต่ละ service คิดค้น variable ที่ไม่มี document ของตัวเอง
ข้อดีข้อเสีย
หัวข้อที่มีชื่อว่า “ข้อดีข้อเสีย”A monorepo (backend + both clients together) vs. a repository per project
- Pros: การเปลี่ยน API กับการเปลี่ยน client ที่ขึ้นต่อกันเป็น atomic commit เดียว review และ merge ด้วยกัน ดังนั้น client ไม่มีทางอ้างถึงรูปร่าง endpoint ที่ไม่มีอยู่บน commit นั้น; clone ที่เดียว, issue tracker ที่เดียว, CI config ที่เดียว; docs และ
.env.exampleตัว shared อยู่กับโค้ดที่อธิบายถึง - Cons: repo ผสม toolchain สามตัว (Python, Dart/Flutter, Node) ดังนั้น tooling และ CI ต้อง scope ต่อ directory แทนที่จะสมมติว่าเป็นภาษาเดียว; การ “run all tests” แบบไร้เดียงสาจริง ๆ แล้วคือสาม test command แยกกัน; และที่สเกลใหญ่มาก ๆ monorepo ต้องใช้ tooling เฉพาะ (build graph, sparse checkout) ที่เกินจำเป็นตรงนี้แต่ควรรู้ว่ามีอยู่
A single shared .env at the repo root vs. a separate .env inside each project
- Pros: ไฟล์เดียวให้เติมหลัง clone, ลิสต์ canonical ตัวเดียวของทุกค่า และไม่มี drift ระหว่างไฟล์ env สามตัวที่เกือบเหมือนกัน; backend กับ script client แบบ local อ่าน Supabase URL ตัวเดียวกันจากที่เดียวได้
- Cons: frontend กับ backend จริง ๆ ไม่ได้ต้องการ variable ตัวเดียวกัน (client ต้องการ Supabase URL กับ anon key; backend ต้องการ database URL กับ JWT secret) ดังนั้นไฟล์เดียวจึงปนเรื่องกัน และคุณต้องระวังว่า client build จะฝังเฉพาะค่า public เท่านั้น — ไม่เอา secret ของ backend มาเด็ดขาด FitTrack เก็บทุกค่าไว้ในไฟล์ที่ documented ไฟล์เดียวแต่บอกชัดใน Auth (Supabase JWT) → ว่าค่าไหน ship ไป client ได้ปลอดภัยและค่าไหนต้องอยู่ฝั่ง server
ติดตั้ง
หัวข้อที่มีชื่อว่า “ติดตั้ง”1. สร้าง directory ของ client
หัวข้อที่มีชื่อว่า “1. สร้าง directory ของ client”จาก repo root (โฟลเดอร์ fittrack/ ที่มี api/ อยู่):
mkdir mobile webทั้งสองจะว่างไปจนถึง Flutter — Foundation → และ Svelte Web Companion →; สร้างตอนนี้แค่เพื่อตรึงโครงสร้างไว้
2. .env.example
หัวข้อที่มีชื่อว่า “2. .env.example”# --- Supabase (from your Supabase project settings) ---# Public values — safe to embed in client apps:SUPABASE_URL=https://your-project-ref.supabase.coSUPABASE_ANON_KEY=your-anon-key
# Server-only values — NEVER ship these to a client:SUPABASE_JWT_SECRET=your-jwt-secretDATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:54322/postgres
# --- API ---API_HOST=0.0.0.0API_PORT=8000บันทึกไฟล์นี้เป็น .env.example ที่ repo root ตัว DATABASE_URL ด้านบนชี้ไปที่ Supabase Postgres แบบ local (port 54322, ค่า default ของ supabase start) — The Supabase project → ตั้งค่านั้นและอธิบายว่าแต่ละค่ามาจากไหน scheme postgresql+asyncpg:// คือ async driver ที่ FastAPI backend ใช้; FastAPI Foundation → ต่อสาย driver ตัวนี้เข้ามา
3. .gitignore
หัวข้อที่มีชื่อว่า “3. .gitignore”# Secrets.env
# Python / apiapi/.venv/__pycache__/
# Flutter / mobilemobile/.dart_tool/mobile/build/
# Node / webweb/node_modules/web/.svelte-kit/web/build/บันทึกไฟล์นี้เป็น .gitignore ที่ repo root บรรทัดที่สำคัญคือบรรทัดแรก: .env ไม่เคยถูก commit — มีแค่ .env.example เท่านั้น ที่เหลือคือ build output ต่อ toolchain
ตรวจสอบผล
หัวข้อที่มีชื่อว่า “ตรวจสอบผล”ยืนยันโครงสร้างจาก repo root:
ls -1.env.example.gitignoreREADME.mdapimobilewebcopy template เป็น .env จริงที่คุณจะเติมในบทถัดไป แล้วยืนยันว่า Git ignore ไฟล์นั้นจริง:
cp .env.example .envgit status --short.env ต้อง ไม่ โผล่ใน output — ถ้าโผล่ แสดงว่าบรรทัดใน .gitignore ไม่ match ส่วน .env.example ควรถูก track อยู่ ความต่างจุดเดียวนั้น — template ถูก commit, secret จริงถูก ignore — คือจุดประสงค์ทั้งหมดของการเช็คนี้
ตรวจสอบความเข้าใจ:
- ทำไมการเอา backend กับ client ทั้งสองไว้ใน repo เดียวถึงทำให้การเปลี่ยน API-บวก-client ปลอดภัยกว่าสาม pull request ที่ต้องประสานงานกันข้ามสาม repo?
api/,mobile/และweb/build แยกกันได้ทั้งหมด ช่องทางเดียวที่mobile/กับweb/มีปฏิสัมพันธ์กับapi/คืออะไร?- ค่าไหนใน
.env.exampleที่ฝังใน client app ที่ ship ได้อย่างปลอดภัย และค่าไหนที่ต้องไม่ออกจาก server เลย? จะเกิดอะไรขึ้นถ้า secret หลุดไปอยู่ใน client build? - หลัง
cp .env.example .envทำไมgit statusถึงต้องแสดง.envเป็น ignored ไม่ใช่ untracked-and-ready-to-commit?
FitTrack repo เป็น monorepo: api/ (FastAPI backend), mobile/ (Flutter, ทีหลัง) และ web/ (Svelte, ทีหลัง) แต่ละตัวเป็นโปรเจกต์ self-contained ที่ build ด้วยตัวเองและคุยกับตัวอื่นผ่าน HTTP เท่านั้น repository เดียวถือทั้งสามเพราะโปรเจกต์นี้เป็นเรื่องของ contract ระหว่าง API กับ client ทั้งสองโดยพื้นฐาน — การเปลี่ยน contract นั้นคือ atomic commit เดียวข้าม api/ กับ client ไม่ใช่ปัญหาการประสานงานข้าม repo .env.example ตัวเดียวที่ root document ทุกค่า config ที่ FitTrack อ่าน แบ่งชัดเป็นค่า public (ปลอดภัยใน client) กับ secret ฝั่ง server เท่านั้น; ตัว .env จริงถูก git-ignore และไม่เคย commit ยืนยันด้วย git status ที่ไม่แสดงไฟล์นั้นออกมา ต่อไป The Supabase project → provision platform ที่จัดการเบื้องหลัง FitTrack แล้วเติมค่าจริงที่ placeholder เหล่านี้แทนอยู่