Clean architecture is a software design approach that emphasizes separation of concerns to create maintainable, scalable, and testable systems. We’ll explore the key principles of Clean Architecture and demonstrate how to implement it in an ASP.NET Core application.
Why Clean Architecture?
Clean Architecture emphasizes separating concerns in an application by organizing the code into layers.
Enhances testability by isolating business logic from external dependencies.
Improves maintainability by making the system easier to understand and modify.
Facilitates scalability by allowing you to adapt to new requirements without disrupting existing code.
The core idea is to place the business logic at the center of the architecture and make it independent of frameworks, UI, and databases.
Layers of Clean Architecture
Clean Architecture typically consists of four main layers. In Clean Architecture, all the layers of the application are either independent or inward-facing, and the core system has no dependencies on any other layer of the system.
Domain Layer
Represents the core business logic and rules.
Contains domain models, entities, and business rules.
Should be independent of any external dependencies.
Application Layer
The application layer contains the business logic. All the business logic will be written in this layer.
Repository interfaces are kept in this layer, separate from their implementation, for loose coupling and separation of concerns.
Service Interfaces and their implementations are also kept in this layer.
DTOs and Auto mappers.
Note: Domain and Application layer are part of the Core.
Core = Domain + Application
This core will be independent of the data access and infrastructure concerns.
Infrastructure Layer
Includes EF Core Db Context, repositories, and external services.
Maintains all the database migrations and database context.
This layer can be further divided into two parts:
Data: Handles migrations and Db Context.
Persistence: Stores repositories.
Presentation/Web API
The Presentation Layer is the outermost layer in Clean Architecture.
Controllers, Filters, Middleware, and Authentication.
Input Validators.
Layers dependency in Clean Architecture:
Domain Layer:
- This is the core layer and should have no dependencies on any other layer.
Application Layer:
- Depends on the Domain Layer to access entities and business logic.
Infrastructure Layer:
- Depends on both the Domain Layer and Application Layer to implement repository interfaces and use domain entities.
Presentation Layer:
- Depends on the Application Layer to call application services.
Dependency Flow:
Domain Layer: No references.
Application Layer: References the Domain Layer.
Infrastructure Layer: References both the Domain Layer and Application Layer.
Presentation Layer: References the Application Layer.
How to create clean architecture based project using visual studio:
Step 1: Set Up the Solution
Create a New Solution in Visual Studio.
Add Multiple Projects:
Domain Project: This will hold the core business logic.
Application Project: This contains the application services and logic.
Infrastructure Project: This handles data access, external services, and repositories.
Presentation Project: This includes controllers, API endpoints, and the user interface layer (if applicable).
Step 2: Folder Structure for Clean Architecture
Here’s how you can structure your folders:
Domain Layer:
This layer contains the core business logic, entities, and interfaces that define the business rules.
Folder Structure:
/Domain /Entities /RepositoryInterfaces
Application Layer:
This layer includes application-specific logic, service interfaces, DTOs (Data Transfer Objects), and application services.
Folder Structure:
/Application /Services /Interfaces /Implementations /DTOs /Mappers
Note:
we can place domain and application in core folder too.
/Core /Domain /Application
Infrastructure Layer:
Data: Handles database context, migrations, and models.
Persistence: Includes the implementation of repositories that interact with the database.
Folder Structure:
/Infrastructure /Data /DbContext /Migrations /Persistence /Repositories
Presentation Layer:
This is the outermost layer and typically contains API controllers, middleware, authentication logic, and filters.
Folder Structure:
/Presentation /Controllers /Middleware /Filters /Validators
Step 3: Code Example for Each Layer
Domain Layer:
Define your core entities, likeUser
orProduct
.
Example (User.cs
):public class User { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } }
Application Layer:
Create service interfaces and DTOs.
Example (IUserService.cs
):public interface IUserService { Task<UserDto> GetUserByIdAsync(int id); }
Infrastructure Layer:
Implement the database context and repositories.
Example (AppDbContext.cs
):public class AppDbContext : DbContext { public DbSet<User> Users { get; set; } }
Example (
UserRepository.cs
):public class UserRepository : IUserRepository { private readonly AppDbContext _context; public UserRepository(AppDbContext context) { _context = context; } public async Task<User> GetByIdAsync(int id) { return await _context.Users.FindAsync(id); } }
Presentation Layer:
Define controllers and API endpoints.
Example (UserController.cs
):[ApiController] [Route("api/[controller]")] public class UserController : ControllerBase { private readonly IUserService _userService; public UserController(IUserService userService) { _userService = userService; } [HttpGet("{id}")] public async Task<ActionResult<UserDto>> GetUser(int id) { var user = await _userService.GetUserByIdAsync(id); if (user == null) return NotFound(); return Ok(user); } }
Step 4: Configuring dependency in Program.cs
:
The Program.cs
file is where you configure dependency injection (DI) to ensure that the various layers of your Clean Architecture interact seamlessly. Below is an example of how to register services, repositories, and other dependencies:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Register Application Layer services
builder.Services.AddScoped<IUserService, UserService>();
// Register Infrastructure Layer dependencies
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<IUserRepository, UserRepository>();
// Add controllers
builder.Services.AddControllers();
// Configure Swagger for API documentation (optional)
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
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();
Step 5: Run and Test the Project
Set up Dependency Injection in
Startup.cs
orProgram.cs
(depending on the .NET version you're using).Run the Application and ensure all layers communicate correctly.
Conclusion
In this article, we implemented clean architecture using Entity Framework and the Code First approach. We now understand how the layers communicate with each other in clean architecture and how to write code for interface repositories and services. With this knowledge, we can develop our projects using clean architecture for API development.