Clean Architecture With ASP.NET Core WebAPI

Introduction

In the article, we are going to discuss and implement the clean using ASP.net Core WebAPI. In the upcoming articles, we will add Entity Framework and presentation layer like Angular, React, etc.

This article can be used by beginners, intermediates, or professionals.

We will cover,

  1. What is Clean Architecture?
  2. Implement Clean Architecture.

Prerequisites 

  1. Visual Studio 2019 or higher
  2. Basic knowledge of ASP.Net Core API

Let’s start with,

What is Clean Architecture? 

Clean architecture was introduced by Robert C. Martin (Uncle Bob) on 13 Aug 2012. 

“Clean architecture is a software design philosophy that separates the elements of a design into levels(layers).” 

Now if I ask you, what would be the design capabilities your application should have, to make the best application design?

 The answer would be:  

The application should have the below abilities,

  1. Testable - Your application should be easily testable.
  2. Separation of concerns - Your application should be loosely coupled.
  3. Independent DB - DB should not tightly bind with other layers.
  4. Independent UI - UI can be changed without changing rest of the system. I mean if I want to change UI framework, we should be able to change without changing any code. Eg. If you want to change Angular framework to React framework, then it should be changed without changing rest of the system.
  5. Independent Framework - Your system should not depend on any library. System should allow you to use such frameworks as tools to remove dependencies heavily. 

Clean Architecture can help us to design a system, with all the above benefits. I can say this is the advantage of the Clean Architecture.

Let’s see the below diagram to understand clean architecture more precisely,

The rule of this architecture says, “source code dependencies can only point inwards

It means as per architecture, dependencies should be from WEB/UI to Interface Rule (CONTROLLER) to Application Business rule (USE CASE) to Domain Layer (ENTITIES) and not vice versa.

More preciously, In the above diagram, the Inner circle cannot know anything about the outer circle. 

I hope you understand clean architecture now. we will create a project to implement this architecture to get a practical understanding.

Implement Clean Architecture

We will create a Member API to get practical experience in Clean Architecture. We will create the below layers,

  1. Member.Domain 
  2. Member.Application
  3. Member.Infrastructure
  4. Member.API

Follow the below steps to implement Clean Architecture, 

Step 1 - Create Entities (Domain Layer) - Member. Domain

We are going to create the most inner layer of the clean architecture. This layer is not dependent on any other layers.

See the below diagram,

Now, will implement this in the visual studio. 

Create a Class library called “Member. Domain” like below,        

Click on the Next Button, and Provide the Project name and solution.

Click on Create Button to complete project creation.

Now we will add a new domain class called Member.cs”.

namespace Member.Domain
{
    public class Member
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Type { get; set; }
        public string Address { get; set; }
        
    }
}

We should add all domain classes in this layer. I am not going to add more classes to make this example simple. Please note that as per our architecture diagram, we have not taken any reference in the “Member. Domain”. 

Step 2 - Create the next layer called “Member.Application”

As per diagram, next Level would be,

Please create a new class library project called “Member. Application” like step1.

Take Reference of the “Member. Domain to Member. Application”.

Now we will create the below class and Interfaces in this layer,

  1. IMemberRepository - Interface
  2. IMemberService - Interface
  3. MemberService - Class

Code would be as per below for all these files,

IMemberRepository.cs

using System.Collections.Generic;

namespace Member.Application
{
    public interface IMemberRepository
    {
        List<Domain.Member> GetAllMembers();
    }
}

IMemberService.cs

using System.Collections.Generic;

namespace Member.Application
{
    //This interface is use for Bussiness Rule / USE CASE
    public interface IMemberService
    {
        List<Domain.Member> GetAllMembers();
    }
}

MemberService.cs

using System.Collections.Generic;

namespace Member.Application
{
    //Implement Bussiness Rule / USE CASES
    public class MemberService : IMemberService
    {
        private readonly IMemberRepository memberRepository;
        public MemberService(IMemberRepository memberRepository)
        {
            this.memberRepository = memberRepository;
        }
        List<Domain.Member> IMemberService.GetAllMembers()
        {
            return this.memberRepository.GetAllMembers();
        }
    }
}

Step 3

Now we will create the next Layer called “Member. Infrastructure” as per the below clean architecture diagram.

Create a new class library “Member. Infrastructure” as we created projects.

This Layer will take reference of “Member.Application” and “Member. Domain” Layer.

Now we will implement “IMemberRepository” in this layer. We will create a new class called “MemberRepository”. For demo purposes, I am hardcoding the Members List in this class.

using Member.Application;
using System.Collections.Generic;

namespace Member.Infrastructure
{
    public class MemberRepository : IMemberRepository
    {
        public static List<Domain.Member> lstMembers = new List<Domain.Member>()
        {
           new Domain.Member{  Id =1 ,Name= "Kirtesh Shah", Type ="G" , Address="Vadodara"},
           new Domain.Member{  Id =2 ,Name= "Mahesh Shah", Type ="S" , Address="Dabhoi"},
           new Domain.Member{  Id =3 ,Name= "Nitya Shah", Type ="G" , Address="Mumbai"},
           new Domain.Member{  Id =4 ,Name= "Dilip Shah", Type ="S" , Address="Dabhoi"},
           new Domain.Member{  Id =5 ,Name= "Hansa Shah", Type ="S" , Address="Dabhoi"},
           new Domain.Member{  Id =6 ,Name= "Mita Shah", Type ="G" , Address="Surat"}
        };
        public List<Domain.Member> GetAllMembers()
        {
            return lstMembers;
        }
    }
}

Step 4 - Now we will create a fourth layer called “Member. API”. 

Let’s create the last layer as per the below architecture diagram.

This layer could be the Web Application, API, WPF, etc. For this demonstration, we are going to use API.

Click on the Next button. In the next screen Provide the API name and location and create a project.

Please set “Member. API” as the default project and add project dependencies.

Before adding a controller to the project, we should add dependencies injection in the “program.cs” file. Please note that we have the “GetAllMember()” method in the “Member. Infrastructure” project and “IMemberRepository.cs” in the “Member. Application” project.

So let's add the below code to the Program.cs file,

using Member.Application;
using Member.Infrastructure;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddScoped < IMemberRepository, MemberRepository > ();
builder.Services.AddScoped < IMemberService, MemberService > ();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) {
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

Now we will add an API Controller called “MembersController.cs”.

using Member.Application;
using Microsoft.AspNetCore.Mvc;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace Member.API.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class MembersController : ControllerBase
    {
        
        private readonly IMemberService memberService;

        public MembersController(IMemberService memberService)
        {
          this.memberService = memberService;
        }
        // GET: api/<MembersController>
        [HttpGet]
        public ActionResult<IList<Domain.Member>> Get()
        {
            return Ok(this.memberService.GetAllMembers());
        }
        
    }
}

In the above code, we have added a constructor to add dependencies injection and the Get() method to get all members.

Now execute your project and notice the output,

Output

That’s all for this article. We have implemented a simple Member API to learn Clean Architecture.

In the next article, we will learn a few more important points about clean architecture.