Dependency Injection
Dependency injection is a design pattern in which an object's dependencies are provided to it from the outside, typically by a framework or container, rather than the object creating those dependencies itself.
Definition
Dependency injection is a design pattern in which an object's dependencies are provided to it from the outside, typically by a framework or container, rather than the object creating those dependencies itself.
Overview
Dependency injection (DI) is a specific technique for applying the broader principle of inversion of control: instead of a class instantiating the services it needs internally, those services are constructed elsewhere and 'injected' into the class, most commonly through its constructor, though setter or property injection are also used. This inversion means a class depends on abstractions — usually interfaces — rather than concrete implementations, which is what makes swapping implementations for testing or configuration possible without touching the class's own code. Frameworks like Spring (Java), Angular, and NestJS build dependency injection into their core architecture using a DI container: a registry that knows how to construct each service and automatically wires dependencies together based on type or configuration, resolving an entire object graph without the developer manually instantiating every collaborator. Even without a formal container, manual dependency injection — simply passing dependencies as constructor arguments — provides most of the same testability benefits. The primary payoff of DI is testability: because a class receives its dependencies rather than creating them, tests can inject mock or fake implementations of those dependencies, isolating the class under test from real databases, network calls, or other side effects. This directly supports unit testing practices and works hand in hand with programming to interfaces rather than concrete classes. DI does add architectural complexity and can make the flow of control harder to trace by reading code alone, since dependencies are wired up externally rather than visible at the point of use — a trade-off that's usually worthwhile in larger applications but can be overkill for small scripts or simple programs.
Key Concepts
- Dependencies are provided externally rather than created internally by a class
- Commonly implemented via constructor injection, setter injection, or property injection
- Specific technique for implementing the broader inversion of control principle
- Frameworks like Spring, Angular, and NestJS provide built-in DI containers
- Enables swapping mock or fake implementations for testing
- Encourages programming against interfaces rather than concrete classes
- Adds architectural indirection that can make control flow harder to trace