Architecture en couches et Clean Architecture en C#
Apprenez l’architecture en couches et Clean Architecture en C# pour concevoir des applications maintenables.
À mesure que les projets logiciels grandissent, il devient de plus en plus important de maintenir un code bien organisé et durable. Pour cela, on utilise l’architecture en couches (Layered Architecture) et son évolution moderne, l’architecture propre (Clean Architecture). Ces deux approches offrent une base solide pour gérer les dépendances, séparer les responsabilités et construire des systèmes testables et extensibles.
Qu’est-ce que l’architecture en couches ?
L’architecture en couches est un modèle classique qui divise une application en plusieurs couches avec des responsabilités distinctes. Sa forme la plus courante comprend 3 ou 4 couches :
- Présentation (UI) – La couche d’interface utilisateur (par ex. Razor Pages, WPF, API Controller).
- Métier (Service / Application) – Contient la logique et les règles métiers.
- Accès aux données (Repository / Infrastructure) – Gère la communication avec la base de données ou des sources externes.
- Noyau / Domaine – Définit les modèles, entités et interfaces de base (couche optionnelle).
La direction des dépendances est du haut vers le bas : l’UI dépend du métier, le métier dépend de l’accès aux données, mais pas l’inverse.
Flux classique d’une architecture en couches
Le schéma suivant illustre un exemple simple :
graph TD;
A[Couche UI] --> B[Couche Métier];
B --> C[Couche Accès aux Données];
C --> D[Base de Données];
Cette structure est suffisante pour des systèmes CRUD simples, mais elle devient complexe lorsque les couches sont trop couplées.
Qu’est-ce que la Clean Architecture ?
L’architecture propre (Clean Architecture), proposée par Robert C. Martin (Uncle Bob), oriente les dépendances vers le centre (le domaine). Son objectif est d’isoler complètement les règles métiers du monde extérieur.
Dans cette architecture, les dépendances vont désormais de l’extérieur vers l’intérieur :
graph LR;
A[UI / API] --> B[Application];
B --> C[Domaine];
C --> D[Infrastructure (Externe)];
- Domaine – Le cœur de l’application : entités, objets de valeur, interfaces.
- Application – Cas d’usage, services et logique métier applicative.
- Infrastructure – Intégrations externes : bases de données, systèmes de fichiers, e-mails, API, etc.
- Présentation – L’interface utilisateur : Web API, site web ou application de bureau.
La différence essentielle : la couche Domaine n’a jamais de dépendance vers les couches externes.
Les couches et leurs responsabilités
-
Domaine : Le cœur de l’application.
Contient les
Entités,Objets de valeuretInterfaces. Les règles métiers y résident. - Application : Orchestre les cas d’usage (Use Cases) à l’aide des interfaces du domaine. Exemples : « Créer une commande », « Valider un paiement ».
- Infrastructure : Fournit les implémentations concrètes des interfaces définies dans Application. (ex. EF Core, SMTP, stockage de fichiers, appels d’API)
- Présentation : La couche la plus proche de l’utilisateur – Web API, MVC ou WPF.
Règle de dépendance
Le principe fondamental de la Clean Architecture : les dépendances pointent toujours vers le centre. Les couches externes connaissent les internes, mais les internes ignorent les externes.
// Couche Domaine (aucune dépendance)
public class Order
{
public int Id { get; set; }
public decimal Total { get; set; }
public bool IsApproved { get; private set; }
public void Approve()
{
if (Total <= 0)
throw new InvalidOperationException("Le montant doit être supérieur à 0.");
IsApproved = true;
}
}
// Couche Application
public interface IOrderRepository
{
Task<Order> GetByIdAsync(int id);
Task SaveAsync(Order order);
}
public class ApproveOrderUseCase
{
private readonly IOrderRepository _repository;
public ApproveOrderUseCase(IOrderRepository repository)
=> _repository = repository;
public async Task ExecuteAsync(int id)
{
var order = await _repository.GetByIdAsync(id);
order.Approve();
await _repository.SaveAsync(order);
}
}
// Couche Infrastructure
public class EfOrderRepository : IOrderRepository
{
private readonly AppDbContext _context;
public EfOrderRepository(AppDbContext context) => _context = context;
public Task<Order> GetByIdAsync(int id) =>
_context.Orders.FirstOrDefaultAsync(o => o.Id == id);
public Task SaveAsync(Order order)
{
_context.Update(order);
return _context.SaveChangesAsync();
}
}
// Couche Présentation (API)
[ApiController]
[Route("api/orders")]
public class OrdersController : ControllerBase
{
private readonly ApproveOrderUseCase _useCase;
public OrdersController(ApproveOrderUseCase useCase) => _useCase = useCase;
[HttpPost("{id}/approve")]
public async Task Approve(int id)
{
await _useCase.ExecuteAsync(id);
return Ok();
}
}
Comme on peut le voir, la couche Domaine ne connaît aucune implémentation d’infrastructure, et la couche Application ne dépend que des interfaces. Les dépendances peuvent ainsi être remplacées facilement (par ex. EF Core ⇢ Dapper).
Gestion des dépendances entre les couches
Les dépendances sont généralement résolues via l’injection de dépendances (DI) dans la couche Infrastructure ou le Composition Root :
// Program.cs ou Startup.cs
builder.Services.AddScoped<IOrderRepository, EfOrderRepository>();
builder.Services.AddScoped<ApproveOrderUseCase>();
Ainsi, le contrôleur ne connaît que ApproveOrderUseCase sans savoir quelle implémentation du dépôt est utilisée.
Différences entre architecture en couches et Clean Architecture
| Caractéristique | Architecture en couches classique | Clean Architecture |
|---|---|---|
| Direction des dépendances | De haut en bas | De l’extérieur vers l’intérieur (vers le noyau) |
| Dépendance à la base de données | La logique métier dépend de la base de données | Le domaine est indépendant de l’accès aux données |
| Testabilité | Difficile | Facile (mock d’interfaces) |
| Évolutivité | Couches fortement couplées | Dépendances gérées via des abstractions |
| Maintenance à long terme | Devient complexe avec le temps | Facile à maintenir, structure modulaire |
Exemple concret : application e-commerce
Ci-dessous, un exemple simplifié d’une architecture Clean Architecture appliquée à un système e-commerce :
- Domaine : Entités et interfaces : Order, Product, Customer.
- Application : Cas d’usage tels que OrderService ou PlaceOrderUseCase.
- Infrastructure : Dépôt basé sur EF Core (EfOrderRepository).
- Présentation : Couche Web API ASP.NET Core.
Ainsi, même si l’interface utilisateur change (API → WPF), les règles métiers (Domain & Application) restent intactes.
Bonnes pratiques
- Gérez les dépendances inter-couches à l’aide d’interfaces.
- La couche Domaine ne doit dépendre d’aucun package externe (uniquement .NET Standard).
- Utilisez des services orientés Use Case (ex.
CreateOrderUseCase). - Les entités doivent contenir la logique métier, pas seulement des données.
- Utilisez des outils comme Mapster ou AutoMapper pour le mapping des DTOs entre les couches.
- Organiser chaque couche comme un projet (assembly) séparé est une bonne pratique.
Exemple de structure de projet
MyApp/
├── MyApp.Domain/
│ ├── Entities/
│ ├── Interfaces/
│ └── Exceptions/
├── MyApp.Application/
│ ├── UseCases/
│ ├── Services/
│ └── DTOs/
├── MyApp.Infrastructure/
│ ├── Persistence/
│ └── Repositories/
└── MyApp.API/
├── Controllers/
└── Program.cs
Cette structure correspond à une implémentation conforme à la Clean Architecture pour une solution .NET modulaire et maintenable.
TL;DR
- Architecture en couches : Modèle classique avec un flux UI → Métier → Données.
- Clean Architecture : Inverse les dépendances – le cœur est le Domaine.
- Domain est indépendant, Application gère le flux, Infrastructure gère les systèmes externes.
- Dependency Injection est la clé de la gestion des dépendances.
- Chaque couche doit avoir une responsabilité claire, être testable et remplaçable.
- La Clean Architecture est idéale pour les systèmes durables, maintenables et évolutifs.
Articles connexes
Modèles de conception en C# (Factory, Singleton, Repository, Observer)
Découvrez les modèles de conception en C#, tels que Factory, Singleton et Repository, pour créer des applications maintenables.
Principes de Clean Code avec C#
Apprenez les principes de Clean Code avec C# pour écrire un code lisible, maintenable et évolutif.
Principes de l’Injection de Dépendances en C#
Apprenez les principes de l’Injection de Dépendances en C#, la gestion des dépendances et le couplage faible avec exemples.
Principes SOLID en C#
Application des principes SOLID en C# avec des exemples : pour un code flexible, maintenable et testable.