Dependency inversion principle
By: Shashi Kumar
Tags: Dependency Inversion, Dependency Injection, Solid, Design Patterns, Software Design Patterns

Dependency Inversion Principle (Inversion of Control)

Dependency inversion concerns itself with decoupling dependencies between high level and low level layers through abstraction.

High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions

Why we need dependency management

Bad Design :

A piece of software that fulfills all its requirements and yet exhibit any or all of the following three traits has a bad design.

It is hard to change because every change affects too many other parts of the system ( Rigidity).

When you make a change, unexpected parts of the system breaks ( Fragility ).

). It is hard to reuse in another application because it cannot be disentangled from the current application (Immobility).

The cause of the bad design :

What is that makes a design rigid, fragile, and immobile. It is the interdependence of the modules within that design.

Traditional software development methods tend to create software architecture in which high level modules depend upon low level modules and in which abstractions depend upon low level modules. Fig1 defines a subprogram hierarchy that describes how the high level modules make calls to lower level modules and their dependency flow.

In the Fig1, we can see that dependency is transitive. A change in the low level method MethodD can force MethodB and MethodA to change.

Therefore we need a mechanism to manage dependency between modules. So that a change in the MethodD can neither affect MethodB nor MethodA to change.

The mechanism is the Dependency Management that brings an abstraction layer between MethodA and MethodB inverting dependency between them. Method MethodA no longer dependent upon MethodB rather both depend upon abstraction. Any changes to MethodB will not force method MethodA to change.

As illustrated in Fig2, we have introduced abstractions IMethodA and IMethodB which breaks direct dependency between (MethodA and MethodB) and (MethodB and MethodD) thereby breaking transitive dependency between them.

It is the high level modules ought to be forcing the low level modules to change. When high level modules are independent of the low level modules, then the high level modules can be reused quite simply.

Thus by inverting dependencies we have created a structure that is simultaneously more flexible, durable and mobile.

Implementation techniques of IOC :

Using service locator pattern

Using dependency injection

Using a contextualized lookup

Using template method design pattern

Using strategy design pattern

I will explain you how to design a decoupled modules by using dependency injection with an example.

Fig 3 Album Application Structure

The above fig 3 is a simple Music Album Application, where high level module Album Application Model and low level module Album Business Model both depend upon the abstraction layer Album Domain Model. Application layer is unaffected by the changes in the Business layer.

We can reuse the Music Album context in Photo Album context which defines the Album Business Model that conforms to the Album Domain Model abstraction layer.

Fig 4 illustrates how the direct dependency between concrete objects AlbumApplicationService and AlbumDomainService is broken and both made to depend upon abstraction IAlbumDomainService.

Fig 4 Dependency inversion by using abstract IAlbumDomainService

Below is an album application structure promotes maintainability, reusability, and flexible to evolve independently without affecting other modules directly.

Here I am using asp.net core’s built-in dependency injection framework to inject dependency objects that dependent objects depend upon. In this way we can create decoupled modules and eventually we have an application structure which is more flexible, durable and mobile.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Threading.Tasks;

using Album.Application.Model;

using Album.Business.Model;

using Album.Domain.Model.Services;

using Microsoft.AspNetCore.Builder;

using Microsoft.AspNetCore.Hosting;

using Microsoft.AspNetCore.HttpsPolicy;

using Microsoft.AspNetCore.Mvc;

using Microsoft.Extensions.Configuration;

using Microsoft.Extensions.DependencyInjection;

using Microsoft.Extensions.Hosting;

using Microsoft.Extensions.Logging;

namespace Album.RestApi

{

public class Startup

{

public Startup(IConfiguration configuration)

{

Configuration = configuration;

}

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.

public void ConfigureServices(IServiceCollection services)

{

services.AddScoped<IAlbumApplicationService, AlbumApplicationService>();

services.AddScoped<IAlbumDomainService, AlbumDomainService>();

services.AddControllers();

}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

{

if (env.IsDevelopment())

{

app.UseDeveloperExceptionPage();

}

app.UseHttpsRedirection();

app.UseRouting();

app.UseAuthorization();

app.UseEndpoints(endpoints =>

{

endpoints.MapControllers();

});

}

}

}

The sample application is posted on my Github account and you can get here.