Dependency Inversion Principle In .NET 6.0

As applications grow in complexity, one of the ways to manage an application is to break it up based on various responsibilities or concerns. The separation of concerns principle helps people to organize their codebase. This will help to organize the code into different layers. You might be familiar with this design approach by the following names:

  • N-Tier
  • N-Layer
  • Or Layers architecture in general.

A few of the benefits of these layering approaches are below

  1. It supports reuse of common low-level functionalities.
  2. Standardizes on implementations.
  3. Enforces restrictions on which layer can communicate with other layers.
  4. Helps to achieve encapsulation.

Though people's intentions with layering approach are good, I have often seen violation of principles. Any time I introduce a change, let us say a database table, it affects the entire application, or at least more than one layer.

In this tutorial, let us review a simple scenario with an example. We will be discussing:

  1. What was done wrong
  2. Then, we will review the golden rule (Dependency Inversion Principle) that we can leverage when separating code into different layers.

Without further delay, let's jump onto the GUI, and create a simple project.

For this tutorial, I’m using the tools below:

  1. Visual Studio Community Edition 2022 Version 17.3.0 Preview 2.0
  2. .NET 6.0
  3. Web API
  4. Swagger

Create a simple ASP.NET Core API project with default template.

Dependency Inversion Principle in .NET 6.0

The solution looks like this.

Let us go ahead and execute this project.

We will have only one endpoint as below.

/weatherforease

Go ahead and run the endpoint and we will be getting the below response.

Dependency Inversion Principle in .NET 6.0

If we go back and look at the solution explorer, we will see that all pages and classes have added a single project.

Now let us see how this project can be refactored into different layers.   The typical layers are below

  1. User Interface (UI)
  2. Business Logic Layer (BLL)
  3. Data Access Layer (DAL)

In this approach,

  • UI will interact with BLL
  • BLL will interact with DAL

Now let us try to simulate these layers into our project.

As a first step, add a new project library and name it WeatherApi.BLL. Later, add another project library called WeatherApi.DAL. The overall solution looks like this, below:

Dependency Inversion Principle in .NET 6.0

The next step would be to set up the dependencies between these projects.

Add WeatherApi.BLL reference into WeatherAPI and Add WeatherApi.DAL into WeatherAPI.BLL.

The below picture will help you to understand the dependencies.

Dependency Inversion Principle in .NET 6.0

Now will start the real game. To begin, let us move the WeatherForcest.cs class from WeatherAPI to WeatherApi.BLL layer. As we are dealing with Weather data, it is obvious that this Model class can be part of the BLL layer. This is also called the Data Transfer Object.

As we are only discussing layering, I’m not going to discuss various terminologies such as Entities, DTOs, POCO models, etc.  We will be focusing only on layering.

For WeatherAPI application to interact with the BLL layer, we need to have a class that helps us to read weather data. Now, let us add a new class into the BLL library called WeatherService. The class looks like this, below:

Dependency Inversion Principle in .NET 6.0

To implement the “Get” method, we need to have a DAL class that will configure some dummy weather data. Let us go ahead and create a new class in the DAL layer called “FakeWeatherDataRepository.cs”. In this class, we need to add a method that will return a dummy collection of Weatherforcecast data. As the DAL doesn’t have the dependencies with the BLL layer, we need to follow either of the below approaches.

  1. Extract the WeatherForecast class and put it in a new project library. Name it something like WeatherApi.Model, and add this as a dependency into the BLL and DAL projects.
  2. Add a WeatherForecast model into the DAL library itself and have the Mapping class in BLL. This might be because the DAL has a structure that maps to our database or an external service.

We are going to follow the second approach. Let us add a new class under DAL and name it WeatherForecastDto.cs.  The class looks like this, below:

Dependency Inversion Principle in .NET 6.0

Here I have removed the Property “TemperatureF” as it has been calculated based on the TemperatureC property.

Now is the time to implement the “Get” method DAL layer.  The method looks like the image below:

Dependency Inversion Principle in .NET 6.0

As a next step, we will use this repository method in BLL layer. To avoid direct dependency on this Fake Repository class, let us extract an interface from this.  To extract an interface, follow the below steps.

  1. Select the class FakeWeatherDataRepository
  2. Go to Edit -> Refactor-> Extract Interface

A pop-up will be displayed, as below:

Dependency Inversion Principle in .NET 6.0

Just click on “OK," and a new interface class will be created in the DAL layer.

The interface will look like the image below:

Dependency Inversion Principle in .NET 6.0

Now, inject this interface into the BLL layer. Post Implementation, the WeatherService class looks like the image below:

Dependency Inversion Principle in .NET 6.0

Now is the time to refactor the interface from the BLL. Follow the same approach we did for the DAL and create an interface.

Dependency Inversion Principle in .NET 6.0

The interface looks like this.

Now let us inject this IWeatherService into Controller.

Dependency Inversion Principle in .NET 6.0

The updated controller looks like this.

Note: Ensure that you are deleting the WeatherForecast.cs class from the WeatherAPI project.

Let us build and ensure that the build is successful. Since we have multiple interfaces injecting into the classes, we need to register these interfaces into the dependency container.

As we are using .NET 6.0, there is no Startup.cs class and dependency registration has been to be incorporated into Program.cs

Dependency Inversion Principle in .NET 6.0

Let us go ahead and execute the program. The below swagger screen will be displayed:

Dependency Inversion Principle in .NET 6.0

Provide the number of days and execute.

Dependency Inversion Principle in .NET 6.0

We got the same response. The response is based on the number of days which we are passing.

If we look at the request flow, it will be top to bottom; ie, the User Interface (Controller) calls the BLL (Business Service), and the BLL calls the DAL (Fake Data Repository). The dependency also flows the same direction, which means the UI depends on the BLL and the BLL depends on the DAL. In other words, the higher layer depends on the implementation details of the lower layer, because it is referring DLL from the lower-level layer.

Though we have extracted the interfaces for decoupling these dependencies, there is a physical dependency on these DLLs. Let us look at this in action. 

Assuming that there is a change in the WeatherForecastDto class to replace the property name “TemperatureC” with “TemperatureInCelsius", due to the change in database.

Now, build the application and see how it behaves. We can see the build got failed and it has the two errors below:

Dependency Inversion Principle in .NET 6.0

One error raised in the BLL layer and another one in the DAL layer.  BusinessService.cs belongs to the BLL library, so why did this break when we had a change in the DAL library? This question leads us to recognize that we were unable to achieve the encapsulation benefit that we discussed at the beginning of this tutorial.  This is happening due to the top-down dependencies of these different layers. In other words, the higher layer is depending on the lower layer.

Now we are going to see how to fix this.

To resolve this, we will apply a design principle called “Dependency Inversion Principle." This principle states that the higher level layer should not depend on the lower layer, and both should depend on abstractions.

For argument’s sake, we could have clarified that we are not depending on the actual implementations, but we have added the interface. If we go back to code and see the BAL layer, there we can see that the DAL library has been referenced. This reference has been added only to consume the IFakeWeatherDataRepository interface. Now, there may be a question as to whether the interface is at the right place?

As per the Dependency Inversion Principle, the ownership of interface should belong to the client. In our case, the BLL is the client. Let us see how this can be achieved.

To begin:

  1. Remove the DAL dependency from BLL library.
  2. Move the IFakeWeatherDataRepository interface to BLL layer.
  3. Update the namespace and Return type of the method “Get”
    Dependency Inversion Principle in .NET 6.0
  4. Make changes in the WeatherService .cs to update the “Get” method
    Dependency Inversion Principle in .NET 6.0
  5. Go to DAL Repository class and fix the dependency issue. Let us go ahead and add the Dependency BLL into the DAL layer.
  6. Go to FakeWeatherDataRepository and make the changes as mentioned below:
    Dependency Inversion Principle in .NET 6.0
  7. Add the DAL dependency into the User Interface Layer.

If you look at the BLL layer, there is no dependency on the DAL Layer. The dependency has been inverted.

If we execute the application, based on the number of days which we are passing, we will get the correct response.

Assume that there is another change in database to update the column Name. We have to replace the property “TemperatureInCelsius” with “Temperature”. Make the necessary changes and build the application. We will see the below two errors only in the DAL Layer.

Dependency Inversion Principle in .NET 6.0

This happened because we have inverted the dependencies between the BLL and DAL layer. The source code has been attached for your perusal.

Now if we look at the architecture, the User Interface is dependent on the Business Layer, and the Data Access layer is also dependent on Business layer. In both cases, the dependencies are flowing inversed, whereas the request flow remains the same.

This kind of architecture is referred to as a Clean Architecture.  It comes with different names which is

  • Clean Architecture.
  • Hexagonal Architecture
  • Onion Architecture
  • Ports and Adapters

The fundamentals behind all the approaches are same.

There are cases where client may not take interface ownership. In our case, we haven’t moved the IWeatherService interface into User Interface (the client).  Because the API is one of the clients, we could have the Web App, Console app or mobile app, consume this weather data. In such a scenario, we need to have a standard Interface like what we have in the IWeatherService in Business Layer in this example.

I hope this article helps you to understand the Dependency Inversion Principle and the overview of Clean Architecture. Thank you for reading this article and please leave your feedback in the comment box below.