All topics
SOLID principles
intermediate

Dependency Inversion Principle (DIP)

High-level modules should not depend on low-level modules. Both should depend on abstractions.

Your business logic should not depend on the details of a database, an HTTP client or a payment provider. Both should depend on an interface in between. That is DIP.

A wall switch doesn't know whether it's connected to a lightbulb, a fan, or a siren. It just toggles power. The switch defines what it needs; each device adapts to that contract.

Key Concepts

1
The problem it solves. OrderService creates a StripeGateway directly. Now you cannot test OrderService without Stripe, and switching provider means editing OrderService.
OrderServiceStripeGateway
2
The fix, and the part people miss. Everyone knows to add an interface. The part that matters is who owns it.
3
    com.shop.domain
        OrderService.java
        PaymentGateway.java      <- the interface lives HERE
4
    com.shop.infra.stripe
        StripeGateway.java       <- implements the domain's interface
5
The infrastructure package imports the domain. The domain imports nothing from infrastructure. That import direction is the whole principle, and you can check it mechanically.
6
If instead the interface sits in the infrastructure package, nothing has been inverted. The domain still depends on a package it does not own, and a change there still reaches inward.
7
Dependency injection is not the same thing. Injection is how the object gets handed in — a constructor parameter, usually. DIP is about which module defines the interface. You can use injection and still have the dependency pointing the wrong way.
8
What it buys you. Tests for OrderService run in microseconds with a stub, no network and no database. And the choice of provider becomes one line in the place where the application is assembled, rather than a fact buried in a hundred files.
OrderService
9
new in the middle of business logic is the smell. It fixes the choice at that spot with no way to substitute. Move construction to the edge — one place that assembles everything — and let the logic receive what it needs.
new
10
Do not put an interface in front of everything. A value object, a date calculation, a utility — these do not vary and do not need faking. Abstract the things that talk to the outside world: databases, HTTP calls, the clock, randomness, email. Interfacing LocalDate arithmetic is ceremony.
LocalDate
11
What the interviewer is probing.1. "What is DIP?" Probing: the direction. Stalls: "Use interfaces." Moves up: both sides depend on an interface, and the interface belongs to the business logic.
12
2. "Is that just dependency injection?" Probing: mechanism versus principle. Stalls: "Yes." *Moves up:* injection supplies the object; DIP decides who owns the interface.
13
3. "Where should the interface live?" Probing: the real test. Stalls: "Next to the implementation." Moves up: with the code that uses it, so the detail depends inward.
14
4. "What does it buy you?" Probing: concrete benefit. Stalls: "Loose coupling." Moves up: tests without infrastructure, and one place to change the implementation.
15
5. "What should you not abstract?" Probing: restraint. Stalls: "Everything." Moves up: value objects, pure functions and stable library types. Abstract what varies or needs faking.
16
  Direction of dependency
17
  Without DIP
      OrderService  ---->  StripeGateway        (policy -> detail)
      // swapping providers edits OrderService
      // testing OrderService needs Stripe, or a network stub
18
  With DIP
      OrderService  ---->  PaymentGateway       (policy -> abstraction)
                              ^
      StripeGateway  --------+                  (detail -> abstraction)
19
      and crucially: PaymentGateway lives in the POLICY module.
20
  Package layout that proves it
21
    com.shop.domain
        OrderService.java
        PaymentGateway.java      <- interface OWNED by the domain
    com.shop.infra.stripe
        StripeGateway.java       <- implements the domain's interface
22
    The infra package imports the domain. The domain imports
    nothing from infra. That import direction IS the principle.
23
  What to put behind a port
    yes   database, HTTP clients, message brokers, the clock,
          randomness, the filesystem, email/SMS providers
    no    value objects, pure functions, java.time arithmetic,
          collections, anything with no IO and no variability
24
  Composition root
25
    // one place assembles the graph; nothing else calls new
    var gateway = new StripeGateway(apiKey, httpClient);
    var service = new OrderService(gateway, clock);