Dependency Inversion Principle
High-level policy should not depend on low-level detail; both depend on abstractions.
The violation
The business logic constructing its own infrastructure:
class OrderService {
private final StripeGateway stripe = new StripeGateway("sk_live_...");
private final PostgresOrderStore db = new PostgresOrderStore("jdbc:...");
// Testing placeOrder() now needs Stripe credentials and a database.
// Switching payment provider means editing the business logic.
}
The policy (what placing an order means) is welded to mechanisms (Stripe, Postgres). The dependency arrows point the wrong way: the most important code in the shop depends on the most replaceable.
The fix
OrderService depends on two abstractions it owns –
PaymentGateway and OrderRepository – and
receives implementations through its constructor, returning a PlacedOrder.
Concrete details like InMemoryOrderRepository implement the
interfaces at the edge.
This inversion is exactly what a DI container (Spring) automates – but the principle is just
constructors and interfaces, no framework required.
Benefits
- Testable business logic – the test injects a fake gateway; no credentials, no database, no mocking framework even.
- Swappable infrastructure – changing payment provider or database is a new adapter class, with zero edits to the service.
- Dependencies point at stability – infrastructure churns; the order-placing policy and its interfaces stay put.