DEV Community

Izzedeen Alfarra
Izzedeen Alfarra

Posted on

Dependency Injection Design Pattern In NestJs

Mastering Dependency Injection: From Chaos to Clean Code

Ever wondered how modern frameworks like NestJS make your code so elegant? Let's dive into Dependency Injection - the design pattern that changed everything!


The Problem: Tightly Coupled Nightmare

Imagine building a house where every room creates its own electricity, plumbing, and heating systems:

// โŒ The Old Way - Tightly Coupled
class UserController {
  getUser() {
    const userRepo = new UserRepository();           // Creating dependencies
    const userService = new UserService(userRepo);   // everywhere!
    return userService.findUser();
  }
}
Enter fullscreen mode Exit fullscreen mode

Problems:

  • ๐Ÿ”„ Creating the same objects repeatedly
  • ๐Ÿ”’ Hard to change or test
  • ๐Ÿ˜ต Each class knows too much about others
  • ๐Ÿ’ธ Memory waste and performance issues

The Solution: Dependency Injection

DI is like having a smart building manager who provides utilities to each room:

// โœ… The New Way - Loosely Coupled
@Injectable()
class UserController {
  constructor(private userService: UserService) {}  // Just receive what you need!

  getUser() {
    return this.userService.findUser();  // Focus on your job only
  }
}
Enter fullscreen mode Exit fullscreen mode

Magic happens: Someone else handles the creation and management!


Meet the DI Container: Your Smart Factory

Think of the DI Container as a smart warehouse manager who:

  • ๐Ÿ“ฆ Stores all your dependencies
  • ๐ŸŽฏ Knows what each class needs
  • โšก Creates instances at the right time
  • โ™ป๏ธ Manages their lifecycle
// NestJS does this automatically!
@Module({
  controllers: [UserController],
  providers: [UserService, UserRepository]  // Register your dependencies
})
Enter fullscreen mode Exit fullscreen mode

Lifecycle Management: When to Create & Destroy

๐Ÿ”„ Singleton (Database Connections)

@Injectable()  // Lives forever - one instance for entire app
class DatabaseService {
  // Expensive to create, shared everywhere
}
Enter fullscreen mode Exit fullscreen mode

โšก Transient (Temporary Workers)

@Injectable({ scope: Scope.TRANSIENT })
class EmailService {
  // New instance every time - destroyed after use
}
Enter fullscreen mode Exit fullscreen mode

๐ŸŽฏ Scoped (Request-Specific)

@Injectable({ scope: Scope.REQUEST })
class UserSessionService {
  // One instance per HTTP request - perfect for user context
}
Enter fullscreen mode Exit fullscreen mode

Real NestJS Example: Clean Architecture

// ๐Ÿ’พ Repository Layer - Data Access
@Injectable()
class UserRepository {
  async findById(id: string) {
    return this.database.query('SELECT * FROM users WHERE id = ?', [id]);
  }
}

// ๐Ÿง  Service Layer - Business Logic  
@Injectable()
class UserService {
  constructor(private userRepo: UserRepository) {}  // โ† DI Magic!

  async getUser(id: string) {
    const user = await this.userRepo.findById(id);
    return this.formatUserData(user);
  }
}

// ๐ŸŽฎ Controller Layer - HTTP Handling
@Controller('users')
class UserController {
  constructor(private userService: UserService) {}  // โ† DI Magic!

  @Get(':id')
  async getUser(@Param('id') id: string) {
    return this.userService.getUser(id);
  }
}
Enter fullscreen mode Exit fullscreen mode

The Beautiful Flow

HTTP Request โ†’ UserController โ†’ UserService โ†’ UserRepository โ†’ Database
     โ†‘              โ†‘              โ†‘              โ†‘
   NestJS        Focuses on     Focuses on    Focuses on
  handles DI    HTTP logic    business logic  data access
Enter fullscreen mode Exit fullscreen mode

Each layer has ONE responsibility!


Why Constructor Injection?

The constructor is the first function called when creating a class - perfect timing to provide dependencies!

class CoffeeMaker {
  constructor(
    private grinder: CoffeeGrinder,    // โ† Received at birth
    private heater: WaterHeater        // โ† Ready to use immediately
  ) {}

  makeCoffee() {
    // Everything I need is already here!
    return this.grinder.grind() + this.heater.heat();
  }
}
Enter fullscreen mode Exit fullscreen mode

Separation of Implementation & Abstraction

// ๐Ÿ“‹ Abstract Contract
interface PaymentProcessor {
  processPayment(amount: number): Promise<boolean>;
}

// ๐Ÿ’ณ Concrete Implementations
@Injectable()
class StripePaymentProcessor implements PaymentProcessor {
  async processPayment(amount: number) {
    // Stripe-specific logic
  }
}

@Injectable()
class PayPalPaymentProcessor implements PaymentProcessor {
  async processPayment(amount: number) {
    // PayPal-specific logic
  }
}

// ๐ŸŽฏ Service doesn't care which implementation!
@Injectable()
class OrderService {
  constructor(private paymentProcessor: PaymentProcessor) {}

  async processOrder(order: Order) {
    return this.paymentProcessor.processPayment(order.total);
  }
}
Enter fullscreen mode Exit fullscreen mode

Switch payment providers without changing a single line in OrderService!


The Amazing Benefits

๐Ÿงช Testing Made Easy

// Mock dependencies for testing
const mockUserRepo = { findById: jest.fn() };
const userService = new UserService(mockUserRepo);
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”„ Easy to Modify

// Switch from MySQL to PostgreSQL? Just change the provider!
@Module({
  providers: [
    // MySQLUserRepository,     โ† Remove
    PostgreSQLUserRepository,   โ† Add
  ]
})
Enter fullscreen mode Exit fullscreen mode

โ™ป๏ธ Resource Efficiency

  • Singleton database connections
  • Transient temporary services
  • Scoped user sessions

๐Ÿ“š Clean, Readable Code

Each class focuses on ONE thing - its actual job!


The Bottom Line

Dependency Injection transforms your code from a tangled mess into a well-orchestrated symphony.

With frameworks like NestJS, you get all this power with just a simple @Injectable() decorator!

Ready to write cleaner, testable, and maintainable code?

Start using Dependency Injection today! ๐Ÿš€


What's your experience with DI? Share your thoughts in the comments! ๐Ÿ‘‡

DependencyInjection #NestJS #CleanCode #SoftwareArchitecture #TypeScript #BackendDevelopment #DesignPatterns #WebDevelopment

Top comments (0)