Skip to content

High-level policy should not be forced to depend directly on a replaceable implementation detail when the system benefits from keeping those decisions independent.

Dependency inversion introduces a stable contract at the boundary. The policy and the implementation depend on that contract instead of the policy importing the implementation directly.

flowchart LR
    Policy[High-level policy] --> Contract[Stable contract]
    Implementation[Replaceable implementation] --> Contract

The dependency arrows point toward the contract. The policy does not need to import the concrete implementation.

This principle is useful at boundaries such as persistence, external services, operating-system integration, delivery mechanisms, or algorithms with several valid implementations.

It is less useful when the dependency is simple, stable, and not expected to vary or require isolated testing.

A good inverted dependency can:

  • keep policy independent from infrastructure choices;
  • make replacement of an external mechanism more local;
  • support isolated tests at a meaningful boundary;
  • make architectural direction explicit.

The abstraction becomes another contract to design and maintain. A weak abstraction can leak the low-level API or grow methods for only one implementation.

Interfaces created only for ceremony add indirection without creating useful independence.

Without an appropriate boundary, policy code can become coupled to database clients, HTTP libraries, cloud SDKs, or other implementation details.

With excessive inversion, every class can receive an interface even when there is no meaningful substitution or policy boundary.

Invert a dependency when the lower-level mechanism is a separate decision that the higher-level policy should not own. Keep direct dependencies when an abstraction would not reduce meaningful coupling.

  • Robert C. Martin. Agile Software Development: Principles, Patterns, and Practices. Prentice Hall, 2002.