Dependency Inversion Principle (DIP)

Definition

High-level modules should not depend on low-level modules.
Both should depend on abstractions.
👉 Depend on interfaces/abstractions, not concrete classes.


Purpose

  • Reduce tight coupling
  • Improve flexibility
  • Enable dependency injection
  • Make unit testing easier
  • Build scalable architecture

Bad Example ❌

Low-Level Class


public class SqlRepository
{
    public void Save()
    {
        Console.WriteLine("Saved to SQL");
    }
}

High-Level Class


public class InvoiceService
{
    private readonly SqlRepository _repository =
        new SqlRepository();

    public void CreateInvoice()
    {
        _repository.Save();
    }
}

Problem

InvoiceService directly depends on:


SqlRepository

If database changes:

  • MongoDB
  • PostgreSQL
  • MySQL

👉 High-level code must change.

This creates:

  • Tight coupling
  • Difficult testing
  • Poor scalability

Good Example ✅

Step 1: Abstraction


public interface IRepository
{
    void Save();
}

Step 2: Low-Level Implementation


public class SqlRepository : IRepository
{
    public void Save()
    {
        Console.WriteLine("Saved to SQL");
    }
}

Another Implementation


public class MongoRepository : IRepository
{
    public void Save()
    {
        Console.WriteLine("Saved to MongoDB");
    }
}

Step 3: High-Level Module


public class InvoiceService
{
    private readonly IRepository _repository;

    public InvoiceService(IRepository repository)
    {
        _repository = repository;
    }

    public void CreateInvoice()
    {
        _repository.Save();
    }
}

Usage Example


IRepository repository =
    new SqlRepository();

var service =
    new InvoiceService(repository);

service.CreateInvoice();

Output


Saved to SQL

Key Concept

Instead of:

Service → SQL Repository

We do:

Service → Interface ← Repository

👉 Loose coupling achieved.


Real-Time Scenario

Payment System:


IPaymentGateway
    ↓
Stripe
PayPal
Razorpay

👉 Change providers without changing business logic.


Advantages

  • ✔ Loose coupling
  • ✔ Easier testing/mock support
  • ✔ Better scalability
  • ✔ Flexible architecture
  • ✔ Easier maintenance

Disadvantages

  • ✖ More interfaces/classes
  • ✖ Slightly increased abstraction complexity

When to Use

Use DIP when:

  • Building enterprise applications
  • Using dependency injection
  • External systems may change
  • Unit testing is important
  • Scalability is required

Real Project Mapping (.NET + Angular)

Feature DIP Usage
Repository pattern DIP
Service injection DIP
Payment gateways DIP
Notification providers DIP
File storage providers DIP

ASP.NET Core Real Example

Dependency Injection Container


builder.Services.AddScoped<
    IRepository,
    SqlRepository>();

👉 ASP.NET Core DI is built around DIP.


Unit Testing Example


var mockRepo = new Mock<IRepository>();

👉 Easy mocking because of abstraction.


DIP vs OCP

DIP OCP
Depend on abstractions Extend without modifying
Focus on dependencies Focus on extensibility
Achieves loose coupling Achieves scalability

Common DIP Violation Signs

  • new Keyword inside services
  • Direct database dependency
  • Hardcoded implementations
  • Difficult unit testing

Pro Tip

DIP works best with:

  • Dependency Injection
  • Repository Pattern
  • Strategy Pattern
  • Clean Architecture
  • Microservices

Summary

DIP helps you:

  • Reduce tight coupling
  • Depend on abstractions
  • Build scalable systems

👉 Perfect for:

  • Enterprise applications
  • APIs
  • Microservices
  • Testable architectures

Comments

Popular posts from this blog

Promises in Angular

Debouncing & Throttling in RxJS: Optimizing API Calls and User Interactions

Csharp Coding - Session