🛑 1. The Problem First: "ป่าช้า Component ที่ทุุกคนกลัวจะย่างกรายเข้าไป"
ลองนึกถึงวันที่บริษัทรับงานเพิ่มจนโปรเจกต์มีไฟล์เป็นหลักพัน:
src/
components/
Button.tsx
UserCard.tsx
Header.tsx
ProductTable.tsx
SpecialDiscountModalForBlackFriday.tsx
... อีก 500 ไฟล์ปนกันมั่วไปหมด
ปัญหา: เมื่อทุุกอย่างถูกกองรวมกันตาม "ประเภท" (Grouping by Type) คุณจะพบว่าเวลาต้องแก้ฟีเจอร์ Checkout คุณต้องวิ่งหาไฟล์ใน 5 โฟลเดอร์ที่อยู่คนละมุมโลก และที่แย่ที่สุดคือคุณไม่กล้าลบไฟล์ที่ไม่ใช้ เพราะไม่รู้ว่าไฟล์ Utils.ts ตัวนั้นมีใครแอบเรียกใช้อยู่ที่มุมไหนของแอปบ้างครับ
💡 2. Real-Life Analogy: การจัดห้องครัวแบบเชฟมืออาชีพ
- Grouping by Type (มือใหม่): เหมือนการจัดห้องครัวโดยเอา "ของที่เป็นสเตนเลส" ไว้ด้วยกัน "ของที่เป็นพลาสติก" ไว้ด้วยกัน เวลาคุณจะทำผัดไทย คุณต้องวิ่งไปหลังบ้านเพื่อหยิบตะหลิว (สเตนเลส) แล้ววิ่งไปหน้าบ้านเพื่อหยิบถ้วย (พลาสติก) เสียเวลาและเหนื่อยเกินจำเป็น
- Feature-based (Senior): เหมือน "สเตชันทำอาหาร". เชฟจะวางมีด เขียง และเครื่องปรุงสำหรับทำซูชิไว้ในโซนเดียวกัน (Feature) ของที่ใช้ด้วยกันต้องอยู่ด้วยกัน (Colocation) เพื่อให้ทำงานได้เร็วที่สุดและไม่ปนกับโซนทำสเต็ก
- Shared Components: เหมือน "อ่างล้างจาน" หรือ "ตู้เย็นกลาง" ที่ทุุกสเตชันต้องเข้ามาใช้ร่วมกัน ของชิ้นไหนที่เป็นพื้นฐานจริงๆ (Dumb Components) ค่อยแยกออกมาไว้ที่ส่วนกลาง
🚀 3. Execution Journey: มหากาพย์การดีไซน์โครงสร้างให้เป็นระเบียบ
Senior จะจัดบ้านโดยคำนึงถึง "การลบ" (Deletability) พอๆ กับ "การสร้าง"
🛠 Step-by-step:
- The Feature Partition: แยกโฟลเดอร์ตาม Business Domain เช่น
features/auth,features/billing,features/inventory - The Internal Encapsulation: ในหนึ่ง Feature ให้มีทั้้ง Components, Hooks, API และ Types ของตัวเอง จบในที่เดียว
- The Public API (index.ts): ใช้ไฟล์
index.tsเป็น "ยามเฝ้าประตู" เพื่อกำหนดว่าฟีเจอร์อื่นสามารถ 'เห็น' หรือ 'ใช้' อะไรจากฟีเจอร์เราได้บ้าง - The Shared Core: แยก UI พื้นฐานที่ไม่มี Business Logic (เช่น Button, Input, Modal) ไว้ในโฟลเดอร์
componentsกลาง
// ✅ Best Practice: Feature-based Structure
src/
features/
product-list/
components/ # คอมโพเนนต์เฉพาะฟีเจอร์นี้
api/ # ฟังก์ชันดึงข้อมูลเฉพาะฟีเจอร์นี้
hooks/ # Logic เฉพาะฟีเจอร์นี้
index.ts # จุด Entry point ของฟีเจอร์ (Public API)
components/ # UI กลาง (Base Components)
🪤 4. The Junior Trap: โรค "Barrel File Import" (Import วนลูปจนแอปค้าง)
จูเนียร์มักจะ Import ของสะเปะสะปะข้ามฟีเจอร์กันไปมา:
// ❌ Junior Trap: ฟีเจอร์ A ไปดึงของจากลึกเข้าไปในฟีเจอร์ B
import { UserAvatar } from '@/features/user-profile/components/UserAvatar';
// 🌋 พัง! เมื่อโปรเจกต์ใหญ่ขึ้น การอ้างอิงข้ามกันสะเปะสะปะแบบนี้จะทำให้เกิด
# Circular Dependency (งูกินหาง) และทำให้ Tool อย่าง Vite หรือ Webpack
# ทำงานช้าลง หรือพังจนแอปไม่รันในตอนจบครับ
ระวัง: อย่าแอบเจาะหลังบ้านเพื่อน
✅ การแก้ไข: จง Import ผ่าน index.ts ของแต่ละ Feature เท่านั้น และถ้าต้องแชร์ข้อมูลกันจริงๆ ให้ย้าย Logic นั้นขึ้นมาไว้ที่หอคอยกลาง (Shared Layer) ครับ
⚖️ 5. The Why Matrix: Grouping by Type vs Feature-based
| หัวข้อ | Grouping by Type (ง่ายตอนเริ่ม) | Feature-based (Senior) |
|---|---|---|
| ความง่ายในการหาไฟล์ | 🐢 ต่ำ (เมื่อไฟล์เยอะขึ้น) | 🚀 สูงสุด (ตาม Business Logic) |
| การทำงานเป็นทีม | ยุ่งยาก (คนมักแย่งแก้โฟลเดอร์เดียวกัน) | 🚀 ดีเยี่ยม (แบ่ง Zone งานได้ชัดเจน) |
| การลบฟีเจอร์ทิ้ง | 🌋 ฝันร้าย (ต้องไล่ลบรายโฟลเดอร์) | 💎 ง่ายมาก (ลบโฟลเดอร์ฟีเจอร์เดียวจบ) |
| ความเร็วในการ Compile | ปานกลาง | ⚡ เร็ว (จัดการ Dependency ได้ดีกว่า) |
🎓 6. Senior Mindset Summary
การเป็น Senior คือการมองว่า "โครงสร้างโปรเจกต์ที่ดี คือโครงสร้างที่เมื่อมองผ่านๆ แล้วคุณรู้ทันทีว่าแอปนี้ 'ทำอะไร' ไม่ใช่แค่รู้ว่าใช้ 'Library อะไร'". การจัดกลุ่มตามพฤติกรรมของธุรกิจจะทำให้โปรเจกต์ของคุณเป็นอมตะและพร้อมรับมือกับการเปลี่ยนแปลงทุุกรูปแบบครับ!