Skip to main content

Dependency Inversion Principle

  • High-level modules should not depend on low-level modules. Both should depend on abstractions.
  • Abstractions should not depend on details. Details should depend on abstractions.
  • In other words, we should depend on abstractions, not on concretions.
class UserManager {
// constructor injection
constructor(database) {
this.database = database;
}

addUser(user) {
this.database.add(user);
}
}

class MySQLDatabase {
add(user) {
console.log(`${user} added to MySQL database`);
}
}

class MongoDBDatabase {
add(user) {
console.log(`${user} added to MongoDB database`);
}
}

const mysql = new MySQLDatabase();
const mongodb = new MongoDBDatabase();

const userManager1 = new UserManager(mysql);
const userManager2 = new UserManager(mongodb);

userManager1.addUser("John Doe");
userManager2.addUser("John Doe");
  • in the above example, the UserManager class is a high-level module, it should not depend on low-level modules, such as MySQLDatabase and MongoDBDatabase

  • the UserManager class should depend on abstractions, such as Database, not on concretions, such as MySQLDatabase and MongoDBDatabase

  • the MySQLDatabase and MongoDBDatabase classes are low-level modules, they should not depend on high-level modules, such as UserManager

// Functional Programming - Dependency Inversion Principle
interface Database {
add(user: string): void;
}

const mysql: Database = {
add: (user) => {
console.log(`${user} added to MySQL database`);
},
};

const mongodb: Database = {
add: (user) => {
console.log(`${user} added to MongoDB database`);
},
};

const userManager1 = (database: Database) => {
const addUser = (user: string) => {
database.add(user);
};

return { addUser };
};

const userManager2 = (database: Database) => {
const addUser = (user: string) => {
database.add(user);
};

return { addUser };
};

userManager1(mysql).addUser("John Doe");
userManager2(mongodb).addUser("John Doe");