A Algo3DVerse
D — Dependency Inversion
SOLID — Principle 5 of 5

Dependency Inversion
Principle

High-level modules should not depend on low-level modules. Both should depend on abstractions. Watch the dependency arrow flip direction in 3D.

Robert C. Martin's fifth principle (two rules): (1) High-level modules should not depend on low-level modules — both should depend on abstractions. (2) Abstractions should not depend on details — details should depend on abstractions. This decouples layers and enables dependency injection.

Watch the arrow flip

Initially OrderService points directly to MySqlDatabase. Press Invert to insert an abstraction layer and flip the dependency.

drag — scroll to zoom

Code example

OrderService directly instantiating MySqlDatabase vs. injecting an IDatabase abstraction. Toggle to compare languages.

DIP VIOLATION ? FIX
// ? BAD — high-level OrderService depends on low-level MySqlDatabase
public class OrderService
{
    private readonly MySqlDatabase _db; // concrete dependency!

    public OrderService()
    {
        _db = new MySqlDatabase(); // hard-wired — can't swap to Postgres
    }

    public void SaveOrder(Order o) => _db.Insert(o);
}

// ? GOOD — both depend on IDatabase abstraction
public interface IDatabase  // the abstraction
{
    void Insert(Order o);
}

public class MySqlDatabase : IDatabase   // detail depends on abstraction
{
    public void Insert(Order o) { /* MySQL specific */ }
}

public class PostgresDatabase : IDatabase // swap in without touching OrderService
{
    public void Insert(Order o) { /* Postgres specific */ }
}

public class OrderService  // high-level, depends on abstraction
{
    private readonly IDatabase _db; // interface — not concrete class

    public OrderService(IDatabase db) // injected from outside
    {
        _db = db;
    }

    public void SaveOrder(Order o) => _db.Insert(o);
}

// Wired up at composition root (startup / DI container)
var service = new OrderService(new MySqlDatabase());
// Or swap to Postgres with zero changes to OrderService:
var service = new OrderService(new PostgresDatabase());
// ? BAD — high-level service creates its own low-level dependency
class OrderService {
    constructor() {
        this._db = new MySqlDatabase(); // hard-wired!
    }
    saveOrder(order) { this._db.insert(order); }
}

// ? GOOD — inject the dependency, depend on the interface
class MySqlDatabase {
    insert(order) { console.log('MySQL insert', order); }
}

class PostgresDatabase {
    insert(order) { console.log('Postgres insert', order); }
}

class InMemoryDatabase { // for testing — no real DB needed
    constructor() { this.records = []; }
    insert(order) { this.records.push(order); }
}

class OrderService {
    constructor(db) {       // receive the abstraction
        this._db = db;
    }
    saveOrder(order) { this._db.insert(order); }
}

// Production
const prod = new OrderService(new MySqlDatabase());

// Test — no database, no network, instant
const test = new OrderService(new InMemoryDatabase());

Key concepts

🔄

Dependency Inversion

high ? abstraction ? low

Neither high-level nor low-level modules depend directly on each other. Both point at a shared abstraction in the middle.

🔧

Abstraction Layer

interface — contract

An interface sits between layers. High-level code calls it without knowing the implementation. Low-level code implements it without knowing who calls it.

💉

Dependency Injection

constructor — DI container

Don't let a class create its own dependencies. Inject them through the constructor (or a DI container) so they can be swapped — including in tests.

🎛

Inversion of Control

IoC — plug-in architecture

Control over which implementations are used moves from the class to the caller. Frameworks, DI containers, and plug-in systems are all IoC in practice.

→ Interface Segregation SOLID Principles — Algo3DVerse Back to Catalog ?
Press ? to begin the narration walkthrough.
0 / 9