SOLID principles
intermediateDependency 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 HERE4
com.shop.infra.stripe
StripeGateway.java <- implements the domain's interface5
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 dependency17
Without DIP
OrderService ----> StripeGateway (policy -> detail)
// swapping providers edits OrderService
// testing OrderService needs Stripe, or a network stub18
With DIP
OrderService ----> PaymentGateway (policy -> abstraction)
^
StripeGateway --------+ (detail -> abstraction)19
and crucially: PaymentGateway lives in the POLICY module.20
Package layout that proves it21
com.shop.domain
OrderService.java
PaymentGateway.java <- interface OWNED by the domain
com.shop.infra.stripe
StripeGateway.java <- implements the domain's interface22
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 variability24
Composition root25
// one place assembles the graph; nothing else calls new
var gateway = new StripeGateway(apiKey, httpClient);
var service = new OrderService(gateway, clock);