Previous OCR In ASP.NET Onion Architecture in .NET Next

Clean Architecture

๐Ÿ—๏ธ Clean Architecture

Clean Architecture is a software design philosophy that organizes code into distinct layers, ensuring that business rules and domain logic are kept independent of external concerns like databases, user interfaces (UI), frameworks, or external services. It was popularized by Robert C. Martin ("Uncle Bob") and emphasizes the separation of concerns to produce applications that are more maintainable, scalable, and testable.

โš™๏ธ Dependency Rule

The central rule of Clean Architecture is the Dependency Rule, which dictates that source code dependencies must always point inward. Code in an inner layer (the core business logic) cannot know anything about the outer layers (implementation details). This is typically achieved through dependency inversion, where inner layers define interfaces that outer layers implement.

๐Ÿ“š Layers of Clean Architecture

The canonical structure of Clean Architecture consists of concentric circles or layers, with each having a specific responsibility.

  • ๐Ÿ”˜ Domain Layer (Entities): The innermost layer, containing core business rules, entities, and data structures. It is completely independent of external concerns like frameworks and databases and encapsulates the most general, high-level business rules that are least likely to change.
  • ๐Ÿงฉ Application Layer (Use Cases): Sits outside the domain layer and orchestrates the flow of data to and from the entities. It contains application-specific business rules, use cases, and interfaces (like repository interfaces) that the outer layers will implement. It is independent of the UI and infrastructure.
  • ๐Ÿ’พ Infrastructure Layer: Implements interfaces and provides concrete implementations for the abstractions defined in the application layer. This is where external services are handled, such as databases (e.g., EF Core DbContext, repository implementations), file storage, and external API clients.
  • ๐Ÿ–ฅ๏ธ Presentation Layer (UI/API): The outermost layer, responsible for handling user interactions, such as an ASP.NET Core Web API or MVC application. It receives requests, sends them through the application layer, and presents the results. It references the application and infrastructure layers but does not contain business logic.

๐Ÿ“ Layers in Clean Architecture

  • Entities: Core business logic and rules
  • Use Cases: Application-specific business logic
  • Interface Adapters: Controllers, presenters, gateways
  • Frameworks & Drivers: UI, database, external APIs

๐Ÿงช Example: To-Do App

  • Entities: Task class with Title, DueDate, IsCompleted
  • Use Cases: CreateTask, CompleteTask, ListTasks
  • Adapters: MVC Controllers, Repositories
  • Frameworks: ASP.NET MVC, SQL Server

๐Ÿง  Example in .NET Core: A Simple Product API

This example outlines the project structure and components for a Clean Architecture Web API using C#.

๐Ÿ“ Project Structure

  • Solution ProductManagement
  • ๐Ÿ“ฆ ProductManagement.Domain (Class Library)
  • ๐Ÿ“ฆ ProductManagement.Application (Class Library)
  • ๐Ÿ“ฆ ProductManagement.Infrastructure (Class Library)
  • ๐ŸŒ ProductManagement.Api (Web API)

๐Ÿท๏ธ Domain Layer (ProductManagement.Domain)

Contains the core Product entity with business rules.

// Entities/Product.cs
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public int Stock { get; set; }

    public Product(string name, decimal price, int stock)
    {
        // Add domain validation here
        if (string.IsNullOrEmpty(name))
            throw new ArgumentException("Name cannot be null or empty.", nameof(name));
        if (price <= 0)
            throw new ArgumentException("Price must be positive.", nameof(price));
        if (stock < 0)
            throw new ArgumentException("Stock cannot be negative.", nameof(stock));

        Name = name;
        Price = price;
        Stock = stock;
    }
}

๐Ÿงฉ Application Layer (ProductManagement.Application)

Contains the use cases, interfaces, and data transfer objects (DTOs). It references only the Domain layer.

// Interfaces/IProductRepository.cs
public interface IProductRepository
{
    Task<Product> GetByIdAsync(int id);
    Task AddAsync(Product product);
    Task UpdateAsync(Product product);
}

// UseCases/CreateProductUseCase.cs (or Command/Query pattern with MediatR)
public class CreateProductUseCase
{
    private readonly IProductRepository _repository;

    public CreateProductUseCase(IProductRepository repository)
    {
        _repository = repository;
    }

    public async Task ExecuteAsync(string name, decimal price, int stock)
    {
        var product = new Product(name, price, stock);
        await _repository.AddAsync(product);
    }
}

๐Ÿ’พ Infrastructure Layer (ProductManagement.Infrastructure)

Implements the repository interface using EF Core. It references the Application and Domain layers.

// Persistence/ApplicationDbContext.cs
public class ApplicationDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }
    // ...
}

// Persistence/ProductRepository.cs
public class ProductRepository : IProductRepository
{
    private readonly ApplicationDbContext _context;

    public ProductRepository(ApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<Product> GetByIdAsync(int id)
    {
        return await _context.Products.FindAsync(id);
    }
    // ... Implement other methods
}

๐Ÿ–ฅ๏ธ Presentation Layer (ProductManagement.Api)

The entry point of the application, typically an ASP.NET Core Web API controller. It references all other layers to resolve dependencies.

// Controllers/ProductController.cs
[ApiController]
[Route("[controller]")]
public class ProductsController : ControllerBase
{
    private readonly CreateProductUseCase _createProductUseCase;

    public ProductsController(CreateProductUseCase createProductUseCase)
    {
        _createProductUseCase = createProductUseCase;
    }

    [HttpPost]
    public async Task<IActionResult&ht; CreateProduct([FromBody] CreateProductDto dto)
    {
        await _createProductUseCase.ExecuteAsync(dto.Name, dto.Price, dto.Stock);
        return CreatedAtAction(nameof(GetProductById), new { id = ... }, null);
    }
}

โœ… Advantages

  • Modifiability and maintainability
  • Scalability
  • Enhanced collaboration
  • Framework independence
  • Separation of concerns
  • High testability

โš ๏ธ Disadvantages

  • Complex for small projects
  • Steep learning curve
  • Performance overhead
  • Verbose setup
  • More boilerplate code
  • Slower initial development

๐Ÿ•น๏ธ When to Use Clean Architecture

  • Large, complex, and long-lived applications
  • Projects with high testability requirements
  • Projects with multiple development teams
  • When anticipating technology changes
  • Systems requiring high test coverage

๐Ÿšซ When Not to Use Clean Architecture

  • Small or simple applications
  • Small or short-lived projects
  • Rapid MVPs or prototypes
  • Projects with tight deadlines and limited resources
  • Teams unfamiliar with the principles

โš–๏ธ Precautions

  • Beware of "over-engineering"
  • Monitor dependency inversion carefully
  • Manage data mapping carefully
  • Ensure team buy-in and training
  • Document layer responsibilities clearly

๐Ÿ’ก Best Practices

  • Start with a clear project structure
  • Embrace dependency injection (DI)
  • Use the Command/Query pattern with MediatR
  • Utilize domain-driven design (DDD)
  • Keep domain entities pure
  • Use templates for consistency
  • Keep business logic pure
  • Define clear interfaces
  • Write unit tests for use cases
  • Avoid leaking implementation details

๐Ÿ’ก Tips

  • Start small and grow the architecture
  • Use templates to reduce boilerplate
  • Refactor regularly to maintain boundaries
  • Educate team with diagrams and examples
Back to Index
Previous OCR In ASP.NET Onion Architecture in .NET Next
*