Repo Layout
สิ่งที่จะสร้าง
หัวข้อที่มีชื่อว่า “สิ่งที่จะสร้าง”repository เดียวชื่อ devblog/ ที่เก็บทั้งสองแอปและโครงสร้างพื้นฐานที่ใช้ร่วมกัน:
devblog/├── apps/│ ├── api/ # NestJS GraphQL API (scaffolded in the next lesson)│ └── web/ # Next.js App Router front end (scaffolded in Lesson 3)├── infra/ # Docker Compose files for local development├── .env # local secrets — never committed├── .env.example # committed template, documents every variable└── .gitignoreบทเรียนนี้สร้างโครงกับไฟล์ระดับ root สองไฟล์ ส่วน apps/api และ apps/web จะให้ CLI ของแต่ละฝั่ง scaffold เองในสองบทเรียนถัดไป ตรงนี้เราจึงสร้างแค่โฟลเดอร์แม่ไว้ก่อน
ทุกโมดูลถัดไปแก้ทั้ง apps/api และ apps/web และทั้งสองฝั่งต้องยึด contract ชุดเดียวกัน: JWT secret, connection string ของ Mongo และ URL ของ API ที่ฝั่ง frontend เรียก การเก็บทั้งคู่ไว้ใน repository เดียวพร้อม root .env ไฟล์เดียว ทำให้ contract นั้นเบี่ยงเบนไม่ได้ มีไฟล์เดียวที่ต้องอัปเดตเมื่อค่าเปลี่ยน และมี git log เดียวที่บอกว่า API กับ UI พัฒนาไปด้วยกันอย่างไร
ข้อดีข้อเสีย
หัวข้อที่มีชื่อว่า “ข้อดีข้อเสีย”สิ่งที่ monorepo ให้คุณตรงนี้ .env และ .gitignore ไฟล์เดียวที่ใช้ร่วมกันทั้งสองแอป infra/docker-compose.yml ไฟล์เดียวที่ orchestrate ฐานข้อมูล API และเว็บแอปพร้อมกัน commit แบบ atomic — แก้ field ใน GraphQL พร้อม query ฝั่ง frontend ที่อ่าน field นั้นได้ใน commit เดียว ไม่ใช่สอง pull request ที่ต้อง merge ให้ถูกลำดับ และ git clone ครั้งเดียวผู้ร่วมงานใหม่ก็ได้ระบบทั้งหมด
สิ่งที่ต้องแลก release ของสองแอปผูกอยู่กับประวัติ repository เดียวกัน ทั้งที่โดยหลักการ deploy แยกกันได้ ประวัติ git ก็ปนกันระหว่างเรื่องของ API กับ UI และ CI ต้องฉลาดเรื่อง path ด้วย (แก้ใน apps/web/ ไม่ควรลาม rebuild apps/api/) ไม่งั้นจะเสียเวลา rebuild ทุกอย่างทุก commit
ทางเลือกอื่น — polyrepo แยก repository ของ api, web และ infra ออกจากกัน แต่ละตัวได้ versioning อิสระ CI pipeline ของตัวเอง และขอบเขตความเป็นเจ้าของที่ชัดเจนถ้าคนละทีมดูแลคนละส่วน ต้นทุนคือสิ่งที่ monorepo ให้พอดี: การรักษา contract JWT/env ให้ตรงกันจะต้องประสานการเปลี่ยนแปลงข้าม repository และไม่มีคำสั่งเดียวที่ยกทั้งสแตกขึ้นมาให้ผู้ร่วมงานใหม่
สำหรับโปรเจกต์สองแอปที่ใช้ contract API เดียวกันและ workflow dev ในเครื่องชุดเดียวกัน monorepo คือทางเลือกที่ถูกต้อง DevBlog เลือกแบบนี้
ติดตั้ง
หัวข้อที่มีชื่อว่า “ติดตั้ง”สร้างโฟลเดอร์ระดับบนสุดและเริ่ม git:
mkdir -p devblog/apps devblog/infracd devbloggit initสร้าง .gitignore ที่ root ของ repo:
node_modules/dist/build/.next/out/coverage/*.log.DS_Store.env.env.local.env.example ตั้งใจไม่อยู่ในลิสต์นี้ เพราะไม่มีความลับอยู่ในนั้นเลย และต้อง track ไว้เพื่อให้ผู้ร่วมงานทุกคนรู้ว่าต้องตั้งตัวแปรอะไรบ้าง
สร้าง .env.example ที่ root ของ repo พร้อมตัวแปรทุกตัวที่สแตกต้องการ:
MONGODB_URI=mongodb://devblog:devblog@localhost:27017/devblog?authSource=adminJWT_SECRET=change-me-in-prodAPI_PORT=4000WEB_ORIGIN=http://localhost:3000NEXT_PUBLIC_API_URL=http://localhost:4000/graphqlแต่ละตัวแปรมีไว้ทำอะไร:
MONGODB_URI— connection string ที่ Mongoose ใช้เชื่อมต่อ MongoDB credentialdevblog:devblogและauthSource=adminตรงกับ root user ที่คุณจะสร้างให้คอนเทนเนอร์ Mongo ใน Compose skeletonJWT_SECRET— secret ที่ Passport-JWT ใช้เซ็นและตรวจสอบ auth token ของผู้เขียนchange-me-in-prodเป็นแค่ placeholder; การ deploy จริงต้อง override ด้วยค่าสุ่มยาว ๆAPI_PORT— พอร์ตที่ NestJS API เปิดฟัง อ่านผ่าน@nestjs/configในmain.tsดูรายละเอียดที่ Backend initWEB_ORIGIN— origin ที่ API ยอมให้เรียกเข้ามา (ก็คือแอป Next.js) ใช้ตอนตั้งค่า CORS ในโมดูลถัดไปNEXT_PUBLIC_API_URL— GraphQL endpoint ที่แอป Next.js เรียก คำนำหน้าNEXT_PUBLIC_เป็นข้อตกลงของ Next.js ที่ทำให้โค้ดฝั่ง client อ่านตัวแปรนี้ได้ด้วย ไม่ใช่แค่ฝั่ง server
สุดท้าย ก๊อป template ไปเป็น .env ตัวจริงที่ติด gitignore ไฟล์นี้แหละที่ process ในเครื่องคุณอ่านจริง ๆ:
cp .env.example .envตรวจสอบผล
หัวข้อที่มีชื่อว่า “ตรวจสอบผล”ls -la devblog# .env .env.example .gitignore apps infra .git
cat devblog/.env.example# (prints the five variables above)
cd devblog && git status# .env is not listed (ignored); .env.example, .gitignore are untracked and ready to commitถ้า .env ไม่ ปรากฏใน git status แต่ .env.example ปรากฏ แสดงว่า .gitignore ของคุณทำงานถูกต้อง
devblog/ เป็น monorepo ที่มี apps/api, apps/web และ infra/ เป็นพี่น้องกัน พร้อม root .gitignore และ .env.example การเลือก monorepo คือการแลก deployment อิสระต่อแอป กับ single source of truth ของ contract JWT/Mongo/API-URL ที่ทั้งสองแอปใช้ร่วมกัน ที่เป็นการตัดสินใจที่ถูกต้องสำหรับสองแอปที่สร้างไปด้วยกัน ตัว .env.example ทำหน้าที่เป็นเอกสารของตัวแปรทุกตัว ส่วน .env จริงเก็บความลับไว้ในเครื่องและไม่ commit ขึ้นไปเลย
ถัดไป: Backend init →