Core Idea
High-level modules should not depend on low-level modules. Both should depend on abstractions.
The Violation
BookingService directly creates a PostgresSeatInventory and StripePaymentGateway. Switching databases or payment providers requires modifying BookingService.
How This Plays in an Interview
Interviewer: "What if we switch from Stripe to Razorpay?"
- Without DIP: "Let me modify
BookingServiceto call Razorpay instead of Stripe." - With DIP: "I implement
RazorpayPayment implements PaymentPort. TheBookingServicedoesn't change. I just inject a different implementation at startup."
When to Apply DIP in LLD Problems
| Problem | What's the Port? | What Implements It? |
|---|---|---|
| Parking Lot (#1) | PricingStrategy | HourlyPricing, WeekendPricing, SeasonalPricing |
| Splitwise (#5) | ExpenseStorage | InMemoryStore, SQLiteStore, CloudSyncStore |
| Vending Machine (#7) | PaymentHandler | CoinPayment, CardPayment, UPIPayment |
| Notification (#18) | NotificationChannel | EmailChannel, SMSPush, WebSocketPush |
| Cache (#31) | EvictionPolicy | LRUEviction, LFUEviction, TTLEviction |
Interview Checklist
- Identify external dependencies (DB, payment, APIs, filesystem, notifications)
- Define boundary interfaces (PORTS) from the HIGH-LEVEL module's perspective
- Implement ADAPTORS as low-level classes
- Wire everything at composition root (never in the high-level module)
- Test high-level logic with fake/mock adaptors
The Fix
BookingService depends on SeatInventoryPort, PaymentPort, NotificationPort โ interfaces defined by the high-level module. Low-level implementations (Postgres, Stripe, Twilio) implement these ports.
// โโโ EXAMPLE 1 โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// WHAT WE ARE IMPLEMENTING:
// A user manager service decouples business logic from specific database
// engine classes.
//
// WHERE THE PRINCIPLE FITS IN:
// High-level UserService depends on the Database abstraction interface rather
// than concrete MySQLDatabase or MongoDBDatabase implementations, enabling
// seamless driver swaps.
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// --- High-level module defines the PORTS (abstractions) ---
interface SeatInventoryPort {
boolean reserve(String seatId, String userId);
void release(String seatId);
}
interface PaymentPort {
boolean charge(String userId, double amount);
void refund(String transactionId);
}
interface NotificationPort {
void send(String userId, String message);
}
// --- High-level business logic depends ONLY on abstractions ---
class BookingService {
private final SeatInventoryPort inventory;
private final PaymentPort payment;
private final NotificationPort notif;
public BookingService(SeatInventoryPort inv, PaymentPort pay, NotificationPort notif) {
this.inventory = inv;
this.payment = pay;
this.notif = notif;
}
public boolean bookTicket(String userId, String seatId, double price) {
if (!inventory.reserve(seatId, userId)) {
System.out.println(" [Booking] Reservation failed: " + seatId);
return false;
}
boolean paid = payment.charge(userId, price);
if (!paid) {
inventory.release(seatId);
System.out.println(" [Booking] Payment failed, released seat");
return false;
}
notif.send(userId, "Booking confirmed for seat " + seatId);
System.out.println(" [Booking] Success: " + seatId);
return true;
}
}
// --- Low-level implementations (injected, not instantiated) ---
class PostgresSeatInventory implements SeatInventoryPort {
public boolean reserve(String seatId, String userId) {
System.out.println(" [Postgres] Reserv seat=" + seatId + " user=" + userId);
return true;
}
public void release(String seatId) {
System.out.println(" [Postgres] Released seat=" + seatId);
}
}
class StripePaymentGateway implements PaymentPort {
public boolean charge(String userId, double amount) {
System.out.println(" [Stripe] Charged " + amount + " for " + userId);
return true;
}
public void refund(String transactionId) {
System.out.println(" [Stripe] Refunded " + transactionId);
}
}
class TwilioNotificationService implements NotificationPort {
public void send(String userId, String message) {
System.out.println(" [Twilio] SMS to " + userId + ": " + message);
}
}
// --- Composition root (wire dependencies) ---
public class Main {
public static void main(String[] args) {
SeatInventoryPort inv = new PostgresSeatInventory();
PaymentPort pay = new StripePaymentGateway();
NotificationPort notif = new TwilioNotificationService();
BookingService booking = new BookingService(inv, pay, notif);
booking.bookTicket("user-42", "A12", 250.0);
}
}Review
Help Us Improve
How helpful was this walkthrough?
Click a star to rate. We actively use this feedback to refine and update our system design content.
Placeholder
Discussion
Share your thoughts, ask questions, or help others.
Loading comments...