Decouple your high-level policy from low-level details. Build software that is flexible, testable, and robust.
"High-level modules should not depend on low-level modules. Both should depend on abstractions."
"Abstractions should not depend on details. Details should depend on abstractions."
The core business logic or "policy" of your application. It decides what needs to happen (e.g., "Process an order").
The implementation details or "mechanisms". They decide how it happens (e.g., "Write to SQL database", "Send SMTP email").
Tightly Coupled
Loosely Coupled
Imagine a house where the light switch is soldered directly to the lamp. To change the appliance, you have to rip open the wall. That's a violation of DIP.
Switch is hard-coded to control the specific Device class directly.
Compare the implementation. Notice how the "Good" version doesn't need to change the High-Level OrderService even when we switch from Email to SMS.
import { EmailSender } from "./EmailSender";
class OrderService {
private sender: EmailSender; // Direct dependency!
constructor() {
// High-level creates Low-level directly
this.sender = new EmailSender();
}
completeOrder(orderId) {
console.log("Order completed");
this.sender.sendEmail("Success!");
}
}
Problem: To use SMS, you must rewrite OrderService. This violates the Open-Closed Principle.
import { INotifier } from "./interfaces";
class OrderService {
private notifier: INotifier; // Depends on Abstraction
// Dependency Injection (via Constructor)
constructor(notifier: INotifier) {
this.notifier = notifier;
}
completeOrder(orderId) {
console.log("Order completed");
this.notifier.notify("Success!");
}
}
Benefit: OrderService never changes. Pass in EmailSender or SmsSender at runtime.
You can easily swap real databases or APIs with "Mock" versions for unit testing. The high-level code won't know the difference.
Want to switch from MySQL to MongoDB? Or send notifications via Slack instead of Email? Just create a new class implementing the interface.
Changes in low-level details (like an API update) are isolated. They don't ripple up and break your core business logic.
Don't confuse the two! DIP is the Principle (the architectural goal). Dependency Injection (DI) is a Design Pattern (the technique) used to achieve it. You use DI (passing objects via constructors) to satisfy the DIP.
In a DIP-compliant architecture, what does the High-Level Module depend on?