Skip to main content

Design Application using Clean Architecture

 Clean Architecture, introduced by Robert C. Martin (Uncle Bob), aims to create systems that are:

  • Independent of frameworks
  • Testable
  • Independent of UI
  • Independent of database
  • Independent of any external agency

Clean Architecture emphasizes separating the concerns of different parts of the system and ensuring that each part has a clear responsibility.

.NET clean architecture as a development approach, available in a layered form to reduce dependency.

The architecture consists of several layers, each with a specific role and dependencies that flow in one direction: from the outer layers to the inner layers.

Inner most Layer is independent to its outer layer, means inner most layer does not have any references, it builds and stand independently.

Dependency Rule

The core principle of Clean Architecture is the Dependency Rule, which states that source code dependencies can only point inward. This means:

  • Nothing in inner circle can depend on anything in an outer circle.
  • Data formats and structures should also follow this rule.

Infrastructure Layer and Presentation Layer are outer most layer so these two layers does not depend on each other.

The main four layers of clean architecture consist of below layers:

  • Domain Layer
  • Application Layer
  • Infrastructure Layer
  • Presentation Layer

I have created project for better understanding, let’s Learn in detail.

I have created below Diagram for better understanding.

Key Points

a. Database implementation happens on Infrastructure layer so consider we connected to SQL database and tomorrow if requirement changes then and client says we need to change database to oracle then we can do with impacting other part of application

b. Similarly, Presentation Layer, today in this example we have used api end point and instead of it we want to use ASP.net MVC or any other UI framework we can use it without impacting rest of application.

Domain Layer

This layer Consist of business Models and entities

Example

This layer will have entities like below

namespace Domain.Entities
{
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
}

Project screen shot

Application Layer

The application layer resides in the middle of the presentation and domain layer and functions as an intermediate between them. Primarily, it maintains the data between corresponding layers but is independent of the infrastructure and presentation layer.

This layer contains Interfaces and classes for all Use cases or Business Logic like below

Application Layer Dependent on domain layer, means this layer need to add reference of Domain layers or entities

Use Cases -> Defining Interfaces for CRUD operations

using Domain.Entities;

using System.Threading.Tasks;

namespace ApplicationLayer.Interfaces
{
public interface ICustomerRepository
{
void Add(Customer customer);
Customer GetById(int id);
IEnumerable<Customer> GetAll();
}
}

Implementation of Use Cases

using Domain.Entities;
using ApplicationLayer.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ApplicationLayer.UseCases
{
public class CreateCustomer
{
private readonly ICustomerRepository _repository;

public CreateCustomer(ICustomerRepository repository)
{
_repository = repository;
}

public void Execute(Customer customer)
{
// Business logic for creating a customer
_repository.Add(customer);
}
}
}

Infrastructure Layer

This layer Contains of DbContext class for database connection and repository classes which will implement ICustomerRepository interface defined on Applicaiton layer.

This layer Depend on Application layer and Domain layer

DB Context classes

using Microsoft.EntityFrameworkCore;
using Domain.Entities;

namespace Infrastructure.Data
{
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{

}

public DbSet<Customer> Customers { get; set; }
}

}

Customer Repository class

using Domain.Entities;
using Infrastructure.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ApplicationLayer.Interfaces;

namespace Infrastructure.Repository
{
public class CustomerRepository : ICustomerRepository
{
private readonly AppDbContext _context;

public CustomerRepository(AppDbContext context)
{
_context = context;
}

public void Add(Customer customer)
{
_context.Customers.Add(customer);
_context.SaveChanges();
}

public Customer GetById(int id)
{
return _context.Customers.Find(id);
}

public IEnumerable<Customer> GetAll()
{
return _context.Customers.ToList();
}
}
}

We can see here Add Use case have reference of application Layer.

Dependency Injection

using Core.Interfaces;
using Infrastructure.Data;
using Microsoft.Extensions.DependencyInjection;

namespace Infrastructure.DI
{
public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(this IServiceCollection services)
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer("YourConnectionString"));

services.AddScoped<ICustomerRepository, CustomerRepository>();

return services;
}
}
}

Presentation Layer

This layer we use to execute our action; this you can call as UI layer but, in our cases, this is Controller class from where user start action.

This layer depends on Domain Layer and Applicaiton Layer only

using Core.Entities;
using Core.UseCases;
using Microsoft.AspNetCore.Mvc;

namespace WebApi.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class CustomerController : ControllerBase
{
private readonly CreateCustomer _createCustomer;

public CustomerController(CreateCustomer createCustomer)
{
_createCustomer = createCustomer;
}

[HttpPost]
public IActionResult Create(Customer customer)
{
_createCustomer.Execute(customer);
return Ok();
}
}
}

Code flow

[User] -> [Web (Controllers)] -> [Core (UseCases)] -> [Core (Interfaces)] -> [Infrastructure (Data)] -> [Database]
  1. User sends a request to add a Customer.
  2. Web Layer (CustomerController) receives the request and converts the input to a domain entity.
  3. Core Layer (Use Case) processes the business logic.
  4. Core Layer (Interface) defines the contract for data operations.
  5. Infrastructure Layer (Data) implements the contract and performs database operations.

Conclusion

We have understood each layer in detail.

Please clap if this is adding any value to your knowledge.

Comments

Popular posts from this blog

Fixing High CPU Usage in .NET Core using Parallelism

  We will fix problem occurs due to incorrect use Parallelism in .NET Core To increase application performance sometimes we use process task or work extensively in Parallel and that may lead performance issue. When we do code, we should understand our requirement very carefully along with hardware resources those we have. We should code like that there should be less CPU usage and server should work without issues. In .NET Core, when you’re using parallelism (like Parallel.For, Task.WhenAll, or Task.Run), you might encounter performance issues if the number of concurrent threads or tasks exceeds what your system can efficiently handle. A common issue is high CPU usage or resource contention, which can degrade overall performance, cause excessive context switching, and even lead to thread starvation or deadlocks. Non members can access from here Effective Parallelism Strategies to Mitigate High CPU Usage in .NET Core We will fix problem occurs due to incorrect use Parallelism in .NE...

Architecting Resilient .NET Core Applications: Handling Transient Failures.

  Transient Failures happen when server is temporarily overloaded.  We need to handle this instead of sending response back to client and say like that “Server is not responding” In the world of Cloud there are temporary failures that may occur in a system and can be recovered from by retrying the operation. T hese failures typically occur due to network issues, temporary unavailability of external services, timeouts, or rate limiting, and they often resolve themselves after a brief period. Possible Reasons for Transient Failures Resource overloaded:   For example, in high-traffic scenarios, limited system resources (like memory or CPU) can lead to failures when trying to access those resources. Network Issues :  Temporary issues like DNS resolution errors, dropped connections, or network congestion can cause operations to fail. Service Unavailability :  External services or databases might be down or unavailable temporarily.  For example, a database connec...