Back to notes
mastery-backend-nestjs
Featured

NestJS Dependency Injection: พลังแห่งเวทมนตร์ Inversion of Control

ไขความลับเบื้องหลัง NestJS: ทำไมเราถึงไม่ต้อง 'new Class()' เอง? เข้าใจ DI Container, Providers และหัวใจของการเขียนโค้ดที่ Test ง่ายระดับ Senior

January 30, 20262 min read readNNexis by Seereen

🛑 1. The Problem First: "โซ่ตรวน" ของการประกอบร่างเอง

ลองนึกถึงโค้ดแบบจูเนียร์ที่พยายามสร้างระบบด้วยมือเปล่า:

HLJS TYPESCRIPT
// ❌ Naive Approach: สั่งสร้าง Service เองภายใน Controller
export class OrderController {
  private orderService: OrderService;

  constructor() {
    // 🌋 พัง! Controller ต้องรู้ว่า OrderService ต้องการ Database อะไรบ้าง
    // ถ้าวันหนึ่ง OrderService เปลี่ยนโครงสร้าง คุณต้องไล่แก้ Controller ทุกตัวที่เรียกใช้!
    const db = new PostgresDriver();
    const repo = new OrderRepository(db);
    this.orderService = new OrderService(repo);
  }
}

ปัญหา: ยิ่งแอปใหญ่ขึ้น การที่ Component หนึ่งต้องรู้ความลับการสร้างของอีก Component หนึ่ง (Tightly Coupled) จะกลายเป็นฝันร้าย เครื่องไม่ออก คุณจะแยกส่วนประกอบเพื่อทำ Unit Test ไม่ได้เลย เพราะทุุกอย่างมันถูกหลอมรวมกันเป็นก้อนเดียว นี่คือสิ่งที่ทำให้โค้ด "ขยับไม่ได้" ครับ


💡 2. Real-Life Analogy: ปลั๊กไฟมาตรฐานโลก

  • The Provider (@Injectable): เหมือน "เครื่องใช้ไฟฟ้า" (ตู้เย็น, ทีวี). มันถูกออกแบบมาให้มีปลั๊กมาตรฐาน และบอกโลกใบนี้ว่า "ฉันพร้อมทำงานนะ ถ้ามีไฟส่งมาให้"
  • The Consumer (Constructor): เหมือน "เต้ารับบนผนัง". Controller ไม่ต้องรู้ว่าไฟฟ้าผลิตมาจากเขื่อนไหน หรือโซลาร์เซลล์อะไร มันแค่บอกว่า "ฉันต้องการไฟฟ้า (Service) มาเสียบตรงนี้"
  • The DI Container (NestJS): เหมือน "การไฟฟ้า". เป็นคนกลางที่คอยเชื่อมต่อสายไฟจากแหล่งผลิต (Provider) มายังเต้ารับของคุณให้โดยอัตโนมัติ คุณแค่มีหน้าที่ "เสียบปลั๊ก" แล้วใช้งานได้เลย

🚀 3. Execution Journey: มหากาพย์การ "ขอของ" (The Inject Flow)

NestJS ใช้ระบบที่เรียกว่า Inversion of Control (IoC)

🛠 Step-by-step:

  1. The Blueprint: แปะป้าย @Injectable() บน Class เพื่อบอก NestJS ว่า "คลาสนี้ให้พี่ดูแลการสร้าง (Manage) นะ"
  2. The Registration: นำ Class นั้นไปใส่ในช่อง providers ของ Module เพื่อลงทะเบียนสินค้าในคลัง
  3. The Request: ใน Controller แค่ประกาศชื่อใน Constructor (ไม่ต้องมีคำว่า new)
  4. The Magic: เมื่อแอปเริ่มรัน NestJS จะเดินไปดูที่คลัง สร้าง Object ให้เสร็จสรรพ แล้วเอามา "ฉีด" (Inject) ใส่ตัวแปรให้เราใช้ฟรีๆ
HLJS TYPESCRIPT
// ✅ Best Practice: ปล่อยให้ Framework จัดการการสร้าง (IoC)
@Injectable() // 🛠 1. ลงทะเบียนเป็น "ผู้ให้บริการ"
export class OrderService {
  findAll() {
    return ["Order 1"];
  }
}

@Controller("orders")
export class OrderController {
  // 🛠 2. แค่บอกว่า "อยากได้" เดี๋ยว NestJS จัดให้เอง!
  constructor(private readonly orderService: OrderService) {}
}

🪤 4. The Junior Trap: โรค "New Class ใน Service"

จูเนียร์มักจะเผลอใช้คำว่า new กับ Utility หรือ Service เมื่อขี้เกียจทำระบบ DI:

HLJS TYPESCRIPT
// ❌ Junior Trap: ทำลายความเป็นอิสระของโค้ด
async function processOrder(data) {
  const logger = new MyCustomLogger(); // 🌋 พัง! คุณไม่สามารถเปลี่ยน Logger เป็นตัวอื่นตอน Test ได้เลย
  logger.log("Processing...");
}

ระวัง: การใช้ new ภายในฟังก์ชันการทำงาน คือการปิดโอกาสในการทำ Mocking! ✅ การแก้ไข: ให้ฉีด Logger เข้ามาทาง Constructor เสมอ เพื่อให้ตอน Test คุณสามารถส่ง "Logger ปลอม" เข้าไปเพื่อเช็คการทำงานได้นั่นเอง


⚖️ 5. The Why Matrix: สร้างเอง vs ให้ระบบฉีดให้

หัวข้อสร้างเอง (Manual new)ให้ระบบฉีด (DI)
การดูแล🐢 ยาก (แก้จุดเดียว กระทบสิบจุด)⚡⚡ ง่าย (แก้ที่เดียว จบทั้งแอป)
การทำ Testนรก (ต้องจำลอง Database จริงตลอด)⚡⚡⚡ สวรรค์ (ส่งของปลอมเข้าไปแทนได้เลย)
Performanceเปลือง RAM (สร้าง Object ซ้ำซ้อน)⚡⚡ ประหยัด (เป็น Singleton โดยธรรมชาติ)
ความสะอาดรก (Constructor ยาวเหยียด)🚀 คลีน (บอกแค่สิ่งที่ต้องการ)

🎓 6. Senior Mindset Summary

การเป็น Senior คือการมองว่า "ความอิสระ (Decoupling) คือพลังที่ยิ่งใหญ่ที่สุดของซอฟต์แวร์". การใช้ Dependency Injection ไม่ใช่แค่เรื่องเวทมนตร์ แต่คือการออกแบบให้ทุกชิ้นส่วนของโค้ด "คุยกันผ่านอินเตอร์เฟซ" ไม่ใช่ "คุยกันผ่านตัวตนจริง" สิ่งนี้จะทำให้ระบบของคุณอยู่ยงคงกระพันไปอีกนานแสนนานครับ!

Share this note

© 2026 My Notes by Seereen