
Dependency Inversion Principle
DIP
“High-level modules should not depend on low-level modules. Both should depend on abstractions.” – Robert C. Martin
“Abstractions should not depend on details. Details should depend on abstractions.”
When to use
Always, when connecting two layers of your application (e.g., Domain Logic connecting to Database, or Controller connecting to Service).
Why it matters
- Decoupling: High-level business rules don’t care about the messy details of databases, APIs, or UI.
- Flexibility: You can swap out a database (MySQL -> Postgres) without changing a single line of business logic.
Signs of Violation
- Using the
newkeyword to instantiate low-level dependencies inside a high-level class. - Importing concrete classes (e.g.,
import MySQLDatabase) in your domain logic.
Explanation
Problem
If high-level modules (business logic) depend directly on low-level modules (utility classes, DB drivers), changes to the low-level details can break the high-level logic.
Solution
Invert the dependency. Define an interface (the abstraction) in the high-level module. Make the low-level module implement that interface. Now both depend on the interface (abstraction).
Real world analogy
You plug your lamp into a wall socket (interface). You don’t solder the lamp directly to the electrical wiring in the wall (implementation). This allows you to plug the lamp into any socket, and the socket to power any device.
Pros and Cons
| Pros | Cons |
|---|---|
Comparison
- Dependency Injection (DI): A pattern/technique used to implement DIP.
- Inversion of Control (IoC): A broader principle where the flow of control is inverted; DIP is a specific form of this related to dependencies.
Code example
Typescript
Bad (Violation)
Good (Adherence)
PHP
Bad (Violation)
Good (Adherence)