Dependency injection (DI) provides objects with their dependencies from the outside instead of creating them internally. Instead of UserService creating its own DatabaseConnection, it receives one through its constructor. This makes code testable (inject mocks), flexible (swap implementations), and loosely coupled.

How Dependency Injection Works

Without DI: class UserService { db = new PostgresDB(); }. With DI: class UserService { constructor(private db: Database) {} }. In tests, inject a mock database. In production, inject PostgresDB. The service doesn't know or care which implementation it uses.

Key Concepts

  • Constructor Injection — Dependencies passed via the constructor — the most common and recommended approach
  • Interface Abstraction — Depend on interfaces, not concrete classes — enables swapping implementations
  • IoC Container — Framework that automatically resolves and injects dependencies — Spring, NestJS, Angular

Frequently Asked Questions

Is DI only for OOP?

The pattern is OOP-focused, but the principle (receive dependencies, don't create them) applies everywhere. Functional programming uses function parameters and closures for the same benefit.