Clean Architecture

Oscar Olsson
3 min readAug 7, 2022

Jason Taylor gives a superb explanation of Clean Architecture on this clip.

Layers in Clean Architecture

Summary

Domain and Application are central to the design. It’s the core of the system.

Presentation and Infrastructure belong to the outermost layer and are not dependent on each other. They only depend on Application.

Application only depends on Domain.

Domain has no dependencies.

Domain

The domain layer contains logic and types used in the application and the surrounding system:

  • Entities
  • Value Objects
  • Enumerations
  • Logic
  • Custom Exceptions

Everything in Core is independent of the outside world and we can easily be 100% unit tested.

Some details:

Use Fluent API in Entity Framework instead of data annotations ([Required], [StringLength]…).

A Value Object is a complex type without an identity. One example is AdAccount. Instead of saving an AdAccount as a string and having to parse the string in different places, you create a simple class: AdAccount with properties Domain and Name and a static function (For) that constructs the object. Parsing and validation now takes place in a central location and it becomes much easier to work with. If someone accidentally tries to create an incorrect AdAccount, we throw a custom exception, AdAccountInvalidException.

When we work with an object of the AdAccount type, we know that it is valid, and that’s a great advantage.

Application

Business logic and types specific to this system

  • Interfaces
  • Models (ViewModels, DTOs)
  • Logic
  • Commands / Queries
  • Validators
  • CustomException

Command Query Responsibility Segregation separates reads (Query) from writes (Commands).

An advantage of CQRS is that it can provide increased performance. But Jason thinks the biggest advantage is that it provides a simpler system. Adding a new feature is easy: just add a Query or Command. Easy to maintain, to make a change you only need to know which Command or Query to change.

CQRS and MediatR is a perfect pair.

With MediatR, Commands and Queries are linked to Requests. So now the Application layer only deals with handling Request and Response. It’s easy to connect events before or after a request like logging, validation, caching or authentication.

The parts that we want to reuse are collected in a Common folder, e.g logging and validation.

In other cases, we want to avoid code reuse. To avoid breaking changes. The code in Jason’s sample project seems inspired by the Vertical Slice Architecture, where all code related to a feature is collected in the same folder (Query, DTO, ViewModel).

All requests that enter the layer are automatically validated. To set up validation, Fluent Validator is used, which is flexible and provides good control. Often, validation requires more logic than we first think. To validate a header, it may not only be a matter of checking the length of the string, but that no previous header already exists in the database.

Infrastructure

This layer contains:

  • Persistence
  • Identity (ASP.NET Core Identity)
  • File System
  • System Clock
  • API Clients

Repository Pattern and Unit of Work don’t need to be implemented if EF Core is used. EF Core’s dbcontext acts as Unit of Work. DbSet acts as a Repository.

Presentation and Infrastructure depend on Core instead of the other way around — in this way the dependency is reversed. This is solved by creating interfaces in the Application Layer that are used by the outer layers. The code in Infrastructure can be replaced with minimal effect since the heart of the system (Domain and Application) is independent of Infrastructure.

Note that the System Clock is seen as an external dependency as well so it belongs to infra.

Presentation

You can select any frontend you want:

  • SPA — Angular, React, Vue
  • Web API
  • Razor Pages
  • MVC
  • Web Forms :)

With the architecture, controllers get very short (often just one line) and contain no or minimal logic. The logic has been moved to the Application Layer as Commands and Queries.

Exceptions are thrown from layers below and we can catch and manage them in a central place and handle them depending on it’s type (e.g ValidationException or NotFoundException).

--

--