System DesignMedium
What does Dependency Inversion look like in C# with DI?
DIP: high-level modules should not depend on low-level modules. Both should depend on abstractions.
Violation: ApplicationLayer depends directly on a database type.
public class OrderService {
private readonly SqlServerOrderRepository _repo = new SqlServerOrderRepository();
public Order Place(Order o) {
_repo.Insert(o);
return o;
}
}
You can't unit-test this without a SQL Server. Swapping to Postgres means editing OrderService.
Inverted: define the abstraction in the domain, implement it in infrastructure.
// Domain — no infrastructure references
public interface IOrderRepository {
Task InsertAsync(Order o, CancellationToken ct);
}
public class OrderService(IOrderRepository repo) {
public Task PlaceAsync(Order o, CancellationToken ct) => repo.InsertAsync(o, ct);
}
// Infrastructure — concrete impl
public class SqlOrderRepository : IOrderRepository {
public Task InsertAsync(Order o, CancellationToken ct) { /* ADO/EF */ }
}
// Composition root
services.AddScoped<IOrderRepository, SqlOrderRepository>();
services.AddScoped<OrderService>();
Now OrderService is testable with a mock and reusable with Postgres, Mongo, an in-memory store — anything that satisfies the contract.
Common mistake: putting IOrderRepository next to SqlOrderRepository in the infrastructure layer. The interface belongs with the consumer (domain), not the implementer. That's what makes it dependency inversion — the direction of dependency flips compared to the runtime call.