Introduction:
There are different types of application architecture like All-in-one architecture, layered architecture, clean architecture, etc. In this article I will explain you how to implement the clean architecture for .NET Core application.
Why clean architecture is one of the best from the list?
Clean architecture 🎯 benefits,
- Heavily based on the design principles.
- Separation of concerns.
- Loose coupling.
- Create maintainable and testable application code.
- It’s completely Independent of external influences like UI and database.
Circular Diagram:

Different circles are used to represent different layers.
In clean architecture we should start from middle of the circle, in middle we have Application Core
- Abstraction (high-level)
- Interface and entities
- Business logic at the center of the application
- Has no dependency on external influences
Outer circle – infrastructure 🏫
- Depend on core and
- Implements interface from core
The UI depends on core.
The Dependencies are inverted which means its pointing inward.
To create this type of architecture we need to follow two important principles.
- Dependency inversion
- Mediator – It used to achieve high level of loose coupling by messaging between different objects.
What does the core project build up with?
- Entites
- Interfaces
- Core
- Infrastructure
- Services
- Exceptions
No dependency to any infrastructure related code
What does the infrastructure project build up with?
- Data access (EF Core)
- Logging
- Identity
- API Clients
- File Access
Complete project structure as shown in the below figure

Application Core Layer🔗 :
Let’s start with Domain project
Add Hotel class under Entities folder
public class Hotel
{
public Guid HotelId { get; set; }
public string Name { get; set; }
}
Create IAsyncRespository class under Contract -> Persistence from application project
public interface IAsyncRespository<T> where T : class
{
Task<T> GetByIdAsync(int id);
Task<T> GetByGuIdAsync(Guid guid);
Task<IReadOnlyList<T>> ListAllAsync();
Task<T> AddAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(T entity);
}
Install below packages for application project
- AutoMapper
- MediatR
Add three classes under Feature->Hotels->Queries->GetHotelList Folder
public class HotelsListVM
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class GetHotelsListQuery : IRequest<List<HotelsListVM>>
{
}
IRequest interface is used to represent a request with a response
public class GetHotelsListQueryHandler : IRequestHandler<GetHotelsListQuery, List<HotelsListVM>>
{
private readonly IAsyncRespository<Hotel> _hotelRepository;
private readonly IMapper _mapper;
public GetHotelsListQueryHandler(IMapper mapper, IAsyncRespository<Hotel> hotelRespository)
{
_mapper = mapper;
_hotelRepository = hotelRespository;
}
public async Task<List<HotelsListVM>> Handle(GetHotelsListQuery request, CancellationToken cancellationToken)
{
var allHotels = (await _hotelRepository.ListAllAsync()).OrderBy(x => x.Name);
return _mapper.Map<List<HotelsListVM>>(allHotels);
}
}
This handler will call the repository to fetch the hotel list from database. IRequestHandeler from MediatR defines a handler for a request.
Create MappingProfile.cs class under Profiles Folder for automapping the domain and view model classes
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Hotel, HotelsListVM>();
}
}
Create ApplicationServiceRegistration class to register this application service with service collections in Application startup.
public static class ApplicationServiceRegistration
{
public static IServiceCollection AddApplicationServices(this IServiceCollection services)
{
services.AddAutoMapper(Assembly.GetExecutingAssembly());
services.AddMediatR(Assembly.GetExecutingAssembly());
return services;
}
}
Packages added for Application core layer.

Infrastructure Layer🏫 :
Create a DBContext class from Infrastructure Project
public class HoteBookingDbContext : DbContext
{
public HoteBookingDbContext(DbContextOptions<HoteBookingDbContext> options) : base(options)
{
}
public DbSet<Hotel> Hotels { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(typeof(HoteBookingDbContext).Assembly);
modelBuilder.Entity<Hotel>().HasData(new Hotel { HotelId = Guid.NewGuid(), Name = "Shaaniya" });
}
}
Implement the IAsyncRepository
Create BasicRepository class under Repository folder from Infrastructure folder.
public class BaseRepository<T> : IAsyncRespository<T> where T : class
{
protected readonly HoteBookingDbContext _dbContext;
public BaseRepository(HoteBookingDbContext dbContext)
{
_dbContext = dbContext;
}
public virtual async Task<T> GetByIdAsync(int id)
{
return await _dbContext.Set<T>().FindAsync(id);
}
public virtual async Task<T> GetByGuIdAsync(Guid guid)
{
return await _dbContext.Set<T>().FindAsync(guid);
}
public async Task<IReadOnlyList<T>> ListAllAsync()
{
return await _dbContext.Set<T>().ToListAsync();
}
public async Task<T> AddAsync(T entity)
{
try
{
await _dbContext.Set<T>().AddAsync(entity);
await _dbContext.SaveChangesAsync();
}
catch (Exception ex)
{
}
return entity;
}
public async Task UpdateAsync(T entity)
{
try
{
_dbContext.Entry(entity).State = EntityState.Modified;
await _dbContext.SaveChangesAsync();
}
catch (Exception ex)
{
}
}
public async Task DeleteAsync(T entity)
{
_dbContext.Set<T>().Remove(entity);
await _dbContext.SaveChangesAsync();
}
}
Create PersistenceServiceRegistration class to register this persistence service in Application startup.
public static class PersistenceServiceRegistration
{
public static IServiceCollection AddPersistenceServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<HoteBookingDbContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("HotelConnectionString")));
services.AddScoped(typeof(IAsyncRespository<>), typeof(BaseRepository<>));
return services;
}
}
Packages add for Infrastructure layer.

API Layer 🕸:
Create a ASP.NET Core Web API project under API folder.
Add HotelsController.cs
[Route("api/[controller]")]
[ApiController]
public class HotelsController : ControllerBase
{
private readonly IMediator _mediator;
public HotelsController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<List<HotelsListVM>>> GetAllHotels()
{
var dtos = await _mediator.Send(new GetHotelsListQuery());
return Ok(dtos);
}
}
In Startup.cs, add ApplicationServiceRegistration and PersistanceServiceRegistration in service collection
public void ConfigureServices(IServiceCollection services)
{
services.AddApplicationServices();
services.AddPersistenceServices(Configuration);
services.AddControllers();
}
Add your database connection string in appsettings.json
"ConnectionStrings": {
"HotelConnectionString": "Server=your server name ;Database= your database name;Integrated Security=False;Persist Security Info=False;User ID= your user id;Password=your password"
},
Packages added for API Project.

Switch to persistence project in package manager console, user below command to apply code first approch for data persistance with SQL Server database.
Add-Migration InitalCommit
Update-database

Check the database

New Record has been inserted
Let’s check the API, run the application
Test the API using POSTMAN

Summary:
We have seen what is clean architecture, what are the benefits of using the clean architecture and its implementation with ASP.NET Core application. We will see how to write the unit test method for this application with clean architecture in my next article.
Source code – GitHub
Happy Coding 👍