เมื่อโปรเจกต์ของคุณเติบโตขึ้นจนมีทั้งฝั่งหน้าบ้าน และหลังบ้าน การแยก Repository หรือการแยกโฟลเดอร์ออกจากกันโดยเด็ดขาดมักจะสร้างความยุ่งยากในการจัดการ Dependency (เช่น ต้องตามอัปเดตไลบรารีแยกกัน, เสียพื้นที่ดิสก์ซ้ำซ้อน)
NPM Workspaces คือฟีเจอร์ที่ติดมากับ Node.js ตั้งแต่เวอร์ชัน 15+ มันถูกออกแบบมาเพื่อทำ Monorepo ช่วยให้เราสามารถรวมหลายๆ โปรเจกต์ ไว้ภายใต้โฟลเดอร์หลักอันเดียว โดยที่สามารถแชร์ node_modules ร่วมกันได้ และยังคงแยกโค้ดของแต่ละฝั่งได้อย่างเป็นสัดส่วน
🏗️ โครงสร้างโฟลเดอร์หลัก
ก่อนจะเริ่มลงมือทำ ให้วางโครงสร้างโฟลเดอร์ของโปรเจกต์ให้ออกมาในลักษณะนี้
my-monorepo/
├── package.json <-- [Root] จุดควบคุมหลักของทุกโปรเจกต์
├── package-lock.json <-- [Root] มีตัวเดียวควบคุมทั้งระบบ
├── node_modules/ <-- [Root] ที่เก็บ Library ทั้งหมด (แชร์ร่วมกัน)
│
├── frontend/ <-- โฟลเดอร์ Frontend (เช่น Vue 3 + Vite)
│ ├── package.json
│ ├── vite.config.ts
│ └── src/
│
└── backend/ <-- โฟลเดอร์ Backend (เช่น Node.js + Express)
├── package.json
└── src/
🛠️ ขั้นตอนการตั้งค่าทีละ Step
สร้าง Root Package.json
ให้สร้างโฟลเดอร์หลักนอกสุดขึ้นมา จากนั้นเปิด Terminal ในโฟลเดอร์นี้แล้วสร้างไฟล์ package.json ตัวหลักขึ้นมา และระบุคีย์ "workspaces" ลงไป
{
"name": "my-monorepo",
"version": "1.0.0",
"private": true,
"workspaces": [
"frontend",
"backend"
],
"scripts": {
"dev:frontend": "npm run dev -w frontend",
"dev:backend": "npm run dev -w backend",
"build:all": "npm run build --workspaces"
}
}
💡 อธิบายคีย์สำคัญ
"private": trueจำเป็นต้องใส่ เพื่อป้องกันไม่ให้เราเผลอ Publish โฟลเดอร์ Root นี้ขึ้นไปยัง npm registry"workspaces"เป็นการบอก NPM ว่ามีโฟลเดอร์ไหนบ้างที่นับเป็นลูกทีมใน Monorepo นี้
ตั้งค่าในโปรเจกต์ลูก
ย้ายโฟลเดอร์โปรเจกต์ของคุณเข้ามาด้านใน หรือสร้างขึ้นใหม่ โดยที่ใน package.json ของโปรเจกต์ลูก ให้ตั้งชื่อ "name" ให้เป็นเอกลักษณ์
frontend/package.json
{
"name": "frontend",
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build"
},
"dependencies": {
"vue": "^3.5.13",
"axios": "^1.8.4"
}
}
backend/package.json
{
"name": "backend",
"version": "0.1.0",
"scripts": {
"dev": "nodemon src/index.js",
"start": "node src/index.js"
},
"dependencies": {
"express": "^4.19.2"
}
}
ลบของเก่าแล้วคลีนระบบ
หากก่อนหน้านี้คุณเคยรัน npm install แยกในโฟลเดอร์ frontend หรือ backend ไปแล้ว ให้ทำการลบโฟลเดอร์ node_modules และ package-lock.json ในโปรเจกต์ลูกออกให้หมด
จากนั้นกลับมาที่โฟลเดอร์นอกสุด แล้วรันคำสั่งnpm install
NPM จะทำการสแกนดู package.json ของตัวลูกทุกตัว แล้วดึงเอา Dependency ทั้งหมดมารวมกันและติดตั้งไว้ที่ node_modules ของ Root ตัวบนสุดเพียงที่เดียว!
🚀 วิธีการรัน Frontend และ Backend พร้อมกัน
เมื่อเปลี่ยนมาใช้ระบบ Workspaces เราไม่จำเป็นต้องเปิด Terminal หลาย ๆ หน้าต่างแล้วคอย cd เข้าโฟลเดอร์นู้นโฟลเดอร์นี้อีกต่อไป เราสามารถควบคุมทุกอย่างจากโฟลเดอร์นอกสุดได้ทันที
วิธีที่ 1: รันพร้อมกันด้วยคำสั่งเดียว
หากต้องการเปิด Terminal อันเดียวแล้วให้มันรันทั้ง Frontend และ Backend ขึ้นมาพร้อมกันในหน้าจอเดียว เราจะใช้เครื่องมือที่ชื่อว่า concurrently เข้ามาช่วย
- ติดตั้ง
concurrentlyไว้ที่โฟลเดอร์ Rootnpm install concurrently --save-dev - ไปเพิ่ม Script ใน
package.jsonตัวนอกสุด"scripts": { "dev:frontend": "npm run dev -w frontend", "dev:backend": "npm run dev -w backend", "dev": "concurrently \"npm run dev:frontend\" \"npm run dev:backend\"" } - เวลาใช้งานจริง แค่พิมพ์คำสั่งเดียวที่ Root
npm run dev
Terminal จะทำการรันทั้งคู่ขึ้นมาพร้อมกัน โดยจะแบ่งสี Log ของหน้าบ้านและหลังบ้านให้เราเห็นอย่างชัดเจนในหน้าต่างเดียว
สั่งรันแยกตัว
หากคุณอยากแยก Terminal คนละหน้าต่างเพื่อดู Log แยกกันชัด ๆ คุณก็ยังสามารถสั่งงานจากโฟลเดอร์ Root ได้โดยใช้ Flag -w
- เปิด Terminal หน้าต่างที่ 1
npm run dev -w frontend - เปิด Terminal หน้าต่างที่ 2
npm run dev -w backend
📦 การจัดการ Dependency
เมื่อใช้ Monorepo เวลาจะลง Library เพิ่ม ห้าม cd เข้าไปในโฟลเดอร์ลูกเด็ดขาด ให้ระบุชื่อ Workspace ปลายทางผ่านโฟลเดอร์ Root เสมอ
- ต้องการลง jsonwebtoken ให้ฝั่ง Backend เท่านั้น
npm i jsonwebtoken -w backend - ต้องการลง primevue ให้ฝั่ง Frontend เท่านั้น
npm i primevue -w frontend - ต้องการลง typescript เป็น Dev Dependency ให้กับทุกโปรเจกต์
npm i typescript -D
🏆 สรุปข้อดีของ NPM Workspaces
- Single Source of Truth: มี
package-lock.jsonเพียงตัวเดียว คุมเวอร์ชันของทั้งระบบ ไม่ต้องกลัวว่าหลังบ้านจะใช้ไลบรารีเวอร์ชันเก่ากว่าหน้าบ้าน - ประหยัดพื้นที่ : หากหน้าบ้านและหลังบ้านใช้ไลบรารีตัวเดียวกัน NPM จะโหลดมาเก็บไว้ที่ Root เพียงครั้งเดียว ไม่โหลดซ้ำซ้อน
- Workflow ที่รวดเร็ว: สามารถสั่ง Build, สั่ง Test หรือสั่ง Run ทุกโปรเจกต์ในระบบได้ผ่านคำสั่งเดียวจากจุดศูนย์กลาง
การเปลี่ยนมาใช้ NPM Workspaces ถือเป็นก้าวแรกที่สำคัญในการทำระบบสถาปัตยกรรมแบบ Monorepo ที่ได้มาตรฐาน และช่วยลดปัญหากวนใจในการจัดการ Codebase ของทีมพัฒนาได้อย่างยั่งยืนครับ
อ่านเพิ่มเติม