A design pattern where objects receive their dependencies from external sources rather than creating them internally.
Dependency Injection (DI) is a design pattern where you give an object its dependencies instead of letting it create them itself. Instead of "I will make what I need," objects say "Give me what I need."
Think of it like a restaurant. Without DI, the chef grows vegetables, raises chickens, and mills flour. With DI, the chef receives pre-prepared ingredients and focuses on cooking.
Without Dependency Injection, classes create their own dependencies internally. This creates tight coupling with several problems: cannot test without real dependencies, cannot reuse with different implementations, hard to change dependencies, and the class controls its own dependencies.
With Dependency Injection, dependencies are passed to the class from outside. Now you control what the class uses, making testing easy and changes simple.
Constructor Injection (most common): Pass dependencies through constructor parameters.
Setter Injection: Set dependencies through setter methods after object creation.
Interface Injection (less common): Dependencies injected through interface methods.
Constructor injection is clearest and most widely used.
Testability: Inject mock dependencies for testing without needing real databases or external services.
: Swap implementations without changing code
No related topics found.
Maintainability: Dependencies are explicit and clear
Reusability: Same class works with different dependencies
Manually injecting dependencies gets tedious. DI containers automate this process.
Popular containers include InversifyJS and TypeDI for Node.js, Spring Framework for Java, .NET Core built-in DI, Symfony and Laravel containers for PHP, and dependency-injector for Python.
Containers register dependencies once, then automatically resolve and inject them when needed.
Angular: Built-in DI system, inject via constructor
NestJS: TypeScript decorators for DI
Spring Boot: Autowired annotation
ASP.NET Core: Constructor injection by default
Modern frameworks have DI baked in.
Define interfaces for dependencies, then inject them through the constructor. Types ensure correct dependencies are provided.
Service Locator is NOT Dependency Injection. It still creates dependencies inside the class by fetching them from a global registry.
This hides dependencies, making code harder to test and understand. Use proper DI instead.
Use DI when:
Skip DI when:
Over-Injection: Injecting too many dependencies (5+ is a code smell - class does too much)
New Operator Inside: Defeats the purpose if you still create dependencies internally
Circular Dependencies: Service A needs B, B needs A - redesign needed
Not Using Interfaces: Inject concrete classes instead of abstractions - reduces flexibility
An email service receives email provider, logger, and rate limiter as dependencies. Same class works in production with real services and in testing with mocks. No code changes needed.
Dependency Injection makes code testable, flexible, and maintainable. Instead of classes controlling their dependencies, you inject dependencies from outside.
Start using DI in your projects. Your future self (and your team) will thank you when tests are easy to write and code is easy to change.