🛑 1. The Problem First: "โค้ดรวมมิตร" (The Spaghetti King)
ลองดูโค้ดแบบจูเนียร์ที่เขียนเสร็จไวแต่แก้ทีไรแล้วร้องไห้:
// ❌ Naive Approach: เขียนทุุกอย่างใน Router/Controller
app.post("/register", async (req, res) => {
const { email, password } = req.body;
// 1. Validation อยู่ที่นี่
if (!email.includes("@")) return res.status(400).send("Invalid email");
// 2. Database Query อยู่ที่นี่
const existing = await db.user.findUnique({ where: { email } });
if (existing) return res.status(400).send("Email exists");
// 3. Business Logic / Hash Password อยู่ที่นี่
const hashed = await bcrypt.hash(password, 10);
// 4. Send Email อยู่ที่นี่... (ยาวไปเรื่อยๆ)
res.send("OK");
});
ปัญหา: เมื่อ Logic ทุกอย่าง (DB, Email, Validation) ไปกองกระจุกอยู่ในที่เดียว ถ้าวันหนึ่งคุณอยากเปลี่ยนจาก Database SQL ไปเป็น MongoDB หรืออยากจะเขียน Unit Test เฉพาะการสมัครสมาชิก คุณจะทำไม่ได้เลยเพราะทุกอย่างมัน "ผูกติดกัน (Tightly Coupled)" จนแกะไม่ออก นี่คือที่มาของ Technical Debt มหาศาลครับ
💡 2. Real-Life Analogy: ห้องเครื่องของเรือยอร์ช
- Controller (ต้นหน): เหมือนคนที่อยู่บนดาดฟ้า คอยรับพิกัด (Request) และบอกว่า "ฉันต้องการไปที่นี่" แต่เขาไม่รู้หรอกว่าเครื่องยนต์ทำงานยังไง
- Service Layer (ช่างเครื่อง): เหมือนคนที่อยู่ในห้องเครื่อง รู้ว่าถ้าจะไปข้างหน้าต้องทำตัวเลขไหน (Business Logic) แต่เขาไม่เคยเห็นน้ำทะเล (HTTP) และไม่แคร์ว่าน้ำมันมาจากถังไหน
- Data Access/Repository (ถังน้ำมัน): เหมือนคนดูแลน้ำมัน มีหน้าที่แค่ "ส่งน้ำมันให้ช่าง" ไม่ต้องรู้ว่าน้ำมันนั้นจะเอาไปขับเคลื่อนเรือหรือเอาไปทำอะไร
🚀 3. Execution Journey: พลังของ 3-Layer Architecture
การแยกชั้น (Decoupling) ช่วยให้โค้ดของคุณพรีเมียมและ Test ง่ายขึ้น 1,000%
🛠 Step-by-step:
- Controller Layer: รับ
req,resแล้วส่งต่อข้อมูลให้ Service (อย่ามี logic!) - Service Layer: เขียน Business Logic ล้วนๆ (คำนวณเงิน, เช็คเงื่อนไขธุรกิจ)
- Repository Layer: คุยกับ Database เท่านั้น (SELECT, INSERT, UPDATE)
// ✅ Best Practice: แยกความรับผิดชอบ (Clean Architecture)
// 🛠 [Controller]
const register = async (req, res) => {
const userData = await userService.signUp(req.body); // 👈 ส่งไม้ต่อให้คนฉลาด
res.status(201).json(userData);
};
// 🛠 [Service]
const signUp = async (data) => {
const exists = await userRepository.findByEmail(data.email); // 👈 คุยกับถังข้อมูล
if (exists) throw new Error("Email Already Taken");
return userRepository.create(data);
};
🪤 4. The Junior Trap: โรค "Controller ฉลาดเกินไป"
จูเนียร์มักจะเผลอเอา req.body หรือ res.status เข้าไปไว้ในชั้น Service:
// ❌ Junior Trap: ผูกติดกับ HTTP ในชั้นที่ไม่ควร
// user.service.js
async function register(req, res) {
// 🌋 พัง! ถ้าวันหลังเราจะสมัครสมาชิกผ่าน CLI หรือ CRON Job
// เราจะรันฟังก์ชันนี้ไม่ได้เพราะมันถามหา 'req' และ 'res'
}
ระวัง: ชั้น Service ต้อง "บริสุทธิ์" (Pure Business Logic) ไม่ควรรู้จักโลกภายนอกอย่าง HTTP เลย ✅ การแก้ไข: รับเฉพาะตัวแปรธรรมดา (String, Object, Array) และส่งค่ากลับเป็น Data เท่านั้น
⚖️ 5. The Why Matrix: ท่าเดิม vs ท่าใหม่
| หัวข้อ | โค้ดรวมมิตร (Flat Structure) | โค้ด 3-Layer (Senior Style) |
|---|---|---|
| การเขียน | ⚡⚡⚡ เร็วมากในวันแรก | ช้ากว่าเล็กน้อย (ต้องสร้างหลายไฟล์) |
| การบำรุงรักษา | 🐢 ยากนรกแตก (แก้หนึ่ง พังเจ็ด) | ⚡⚡ ง่าย (แยกส่วนกันชัดเจน) |
| การทำ Unit Test | แทบทำไม่ได้ | ⚡⚡⚡ ทำได้ 100% (Mock ง่ายมาก) |
| การขยายระบบ | ต้อง Code ใหม่หมด | ⚡⚡⚡ เปลี่ยน DB หรือ API สบายๆ |
🎓 6. Senior Mindset Summary
การเป็น Senior คือการมองว่า "เราไม่ได้เขียนโค้ดเพื่อให้คอมพิวเตอร์รันได้ แต่เราเขียนเพื่อให้คน (รวมถึงตัวเราในอนาคต) อ่านรู้เรื่องและแก้ไขได้". การจัดโครงสร้างที่ดีคือการลงทุนที่คุ้มค่าที่สุดในระยะยาวของโปรเจกต์ครับ!