Learn Clean Architecture in .NET

Introduction

Clean Architecture is a software design philosophy that emphasizes the importance of creating systems that are easy to maintain, test, and understand. It achieves this by adhering to principles like separation of concerns, dependency inversion, encapsulation, and testability. This guide will explore how to implement Clean Architecture in a .NET project, ensuring your codebase remains organized, modular, and scalable.

Key principles of Clean Architecture

  1. Separation of Concerns: Each part of the system should have a single responsibility. This makes the system easier to understand and maintain.
  2. Dependency Inversion: High-level modules should not depend on low-level modules. Both should depend on abstractions, creating a loosely coupled system.
  3. Encapsulation: Modules should hide their internal implementation details and expose only what is necessary.
  4. Testability: The system should be designed in a way that makes it easy to write automated tests.

Structure of Clean Architecture in .NET

Clean Architecture typically involves organizing the code into several distinct layers.

  1. Core Layer
    • Domain Entities: Core business objects of the application, usually represented as POCO (Plain Old CLR Objects) classes containing business logic.
    • Interfaces: Abstractions for repositories and services, to be implemented in other layers.
    • DTOs (Data Transfer Objects): Simple objects used to transfer data between layers.
  2. Application Layer
  3. Use Cases / Services: Contain application-specific business rules, orchestrating data flow between entities and other system parts.
  4. Validators: Handle validation logic for the use cases.
  5. Mappers: Map between domain entities and DTOs.
  6. Infrastructure Layer
    • Repositories: Implement repository interfaces defined in the core layer, handling data persistence and retrieval.
    • Third-Party Services: Integrations with external services like email providers, payment gateways, etc.
    • Configurations: Configuration settings for the application.
  7. Presentation Layer
    • Controllers: Handle HTTP requests in a web application, such as those using ASP.NET Core.
    • Views: UI components, including Razor views and HTML pages.
    • ViewModels: Models specifically designed for use by the views.

Example Structure in a .NET Project

Below is an example folder structure for a .NET project adhering to Clean Architecture principles.

Solution
|-- src
|   |-- Core
|   |   |-- Entities
|   |   |   |-- User.cs
|   |   |-- Interfaces
|   |   |   |-- IUserRepository.cs
|   |   |-- DTOs
|   |   |   |-- UserDto.cs
|   |-- Application
|   |   |-- Services
|   |   |   |-- UserService.cs
|   |   |-- Validators
|   |   |   |-- UserValidator.cs
|   |   |-- Mappers
|   |   |   |-- UserMapper.cs
|   |-- Infrastructure
|   |   |-- Repositories
|   |   |   |-- UserRepository.cs
|   |   |-- Services
|   |   |   |-- EmailService.cs
|   |-- Presentation
|       |-- Controllers
|       |   |-- UserController.cs
|       |-- Views
|       |-- ViewModels
|-- tests
|   |-- Core.Tests
|   |-- Application.Tests
|   |-- Infrastructure.Tests
|   |-- Presentation.Tests

Implementing Clean Architecture in .NET

Here is a simple example of implementing a User service in a .NET project.

Domain Entity (Core/Entities/User.cs).

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

Repository Interface (Core/Interfaces/IUserRepository.cs).

public interface IUserRepository
{
    User GetUserById(int id);
    void AddUser(User user);
}

User Service (Application/Services/UserService.cs).

public class UserService
{
    private readonly IUserRepository _userRepository;
    public UserService(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }
    public UserDto GetUserById(int id)
    {
        var user = _userRepository.GetUserById(id);
        return UserMapper.MapToDto(user);
    }
    public void CreateUser(UserDto userDto)
    {
        var user = UserMapper.MapToEntity(userDto);
        _userRepository.AddUser(user);
    }
}

User Mapper (Application/Mappers/UserMapper.cs).

public static class UserMapper
{
    public static UserDto MapToDto(User user)
    {
        return new UserDto
        {
            Id = user.Id,
            Name = user.Name,
            Email = user.Email
        };
    }
    public static User MapToEntity(UserDto userDto)
    {
        return new User
        {
            Id = userDto.Id,
            Name = userDto.Name,
            Email = userDto.Email
        };
    }
}

User Controller (Presentation/Controllers/UserController.cs).

[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
    private readonly UserService _userService;
    public UserController(UserService userService)
    {
        _userService = userService;
    }
    [HttpGet("{id}")]
    public ActionResult<UserDto> GetUserById(int id)
    {
        var user = _userService.GetUserById(id);
        if (user == null)
        {
            return NotFound();
        }
        return Ok(user);
    }
    [HttpPost]
    public ActionResult CreateUser(UserDto userDto)
    {
        _userService.CreateUser(userDto);
        return CreatedAtAction(nameof(GetUserById), new { id = userDto.Id }, userDto);
    }
}

Conclusion

By following Clean Architecture principles in your .NET projects, you can create systems that are more maintainable, testable, and scalable. This structure ensures that each part of your application has a single responsibility, leading to a more organized and modular codebase. Implementing Clean Architecture may require an initial investment of time and effort, but the benefits of having a well-structured and easy-to-maintain codebase are well worth it in the long run.


Similar Articles
Citiustech Healthcare Technology Pvt Ltd
CitiusTech plays a deep and meaningful role in powering the future of healthcare.