LLD Hub
Learn

Dependency InversionDepend on abstractions, not concretions

High-level code depends on interfaces; wiring happens at the edge. · Constructor injection makes tests use mocks easily.

Watch

Watch, then scroll down for code and practice.

In code

Invert the dependencyTypeScript
interface UserRepo {
  findById(id: string): Promise<User | null>;
}

class UserService {
  constructor(private repo: UserRepo) {}

  async getName(id: string) {
    const u = await this.repo.findById(id);
    return u?.name ?? "unknown";
  }
}

📘 Key ideas

The principle

High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details.

The smell

UserService does this.db = new MySQLDatabase() in its constructor. Switching to Postgres requires editing UserService.

Constructor injection

Pass the IUserRepository interface into UserService's constructor. The caller decides which implementation to inject. UserService never imports a concrete DB class.

Benefit: testability

Inject a MockUserRepository in tests. No real database needed. Tests run fast and in isolation.

🧠 Practice — Apply What You Learned

🚀 Now apply what you learned

Pick a problem above, write your solution, and get AI feedback on your design.

Start Practice →