Le grand flou architectural
Dans 90 % des projets, on entend :
« On fait du DDD. »
« Non, on fait du CQRS. »
« Non, c’est une architecture hexagonale ! »
… alors qu’en réalité, le projet est juste un CRUD bien rangé en couches.
Ces concepts sont complémentaires, mais pas interchangeables.
Ils agissent à différents niveaux de conception : structure, séparation de responsabilités, organisation du code et modélisation du métier.
4 familles de patterns
| Concept | Type | Objectif principal |
|---|---|---|
| DDD (Domain-Driven Design) | Méthodologie / modélisation | Refléter la réalité métier dans le code |
| Vertical Slice Architecture | Organisation du code | Structurer par fonctionnalité (use case) plutôt que par couche |
| CQRS | Pattern d’exécution | Séparer lecture et écriture |
| Hexagonale (Ports & Adapters) | Style d’architecture | Isoler le domaine des détails techniques |
L’architecture en couches (Layered CRUD)
Le but est de séparer les responsabilité techniques.
Comme structurer son projet ainsi :
/Controllers
/Services
/Repositories
/Models
C’est simple et rapide à mettre en place, suffisant pour du CRUD simple. L’inconvénient c’est qu’on se retrouve confronter à certaines limites comme le métier dissous dans des « services » génériques, des dépendances qui remontent vers le haut et un code qui devient rapidement rigide.
Le Layered Crud, pertinent pour les projets data-centric.
Diagramme — Architecture en couches

Vertical Slice Architecture — penser “use case”
Type : Feature-centric
Organiser le code par fonctionnalité, pas par couche technique.
Pertinent pour des projets modulaires ou évolutifs.
/Features
/Users
/Create
CreateUserCommand.cs
CreateUserHandler.cs
CreateUserEndpoint.cs
/GetById
GetUserQuery.cs
GetUserHandler.cs
GetUserEndpoint.cs
Exemple :
public record CreateUserCommand(string Email, string Password) : IRequest<Guid>;
public class CreateUserHandler : IRequestHandler<CreateUserCommand, Guid>
{
private readonly IUserRepository _repo;
public CreateUserHandler(IUserRepository repo) => _repo = repo;
public async Task<Guid> Handle(CreateUserCommand cmd, CancellationToken ct)
{
var user = new User(cmd.Email, cmd.Password);
await _repo.AddAsync(user, ct);
return user.Id;
}
}
Forces :
- Chaque slice est indépendante et testable
- Structure claire : un use case = un dossier
- Parfait avec MediatR et Minimal APIs
Limites :
- Nécessite rigueur pour garder les slices cohérentes
- Risque d’éparpillement si le projet grossit sans guidelines
Diagramme – Organisation Vertical Slice

voir l’article => https://xavierraboteur.fr/2025/10/14/vertical-slice-modular-monolith-le-sweet-spot/
CQRS — Command Query Responsibility Segregation
Type : Behavior-centric
Idéal pour les systèmes à fort volume ou fort métier.
CQRS (Command Query Responsibility Segregation) ne parle ni de couches, ni de domaine.
Il dit simplement : séparons l’écriture et la lecture.
- Command → modifie l’état (pas de retour riche)
- Query → lit l’état (sans effet de bord)
/Features
/Orders
/CreateOrder (Command)
/GetOrderById (Query)
Exemple :
// Command
public record CreateOrderCommand(Guid CustomerId, decimal Amount) : IRequest<Guid>;
// Query
public record GetOrderByIdQuery(Guid Id) : IRequest<OrderDto>;
// Command Handler
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, Guid>
{
private readonly IOrderRepository _repo;
public async Task<Guid> Handle(CreateOrderCommand cmd, CancellationToken ct)
{
var order = new Order(cmd.CustomerId, cmd.Amount);
await _repo.AddAsync(order);
return order.Id;
}
}
// Query Handler
public class GetOrderByIdHandler : IRequestHandler<GetOrderByIdQuery, OrderDto>
{
private readonly AppDbContext _db;
public Task<OrderDto> Handle(GetOrderByIdQuery q, CancellationToken ct)
=> _db.Orders
.Where(o => o.Id == q.Id)
.Select(o => new OrderDto(o.Id, o.Amount))
.FirstAsync(ct);
}
Schéma de flux CQRS

Avantages :
- Clarifie les intentions : lire ≠ modifier
- Facilite l’optimisation des modèles de lecture
- Compatible avec Event Sourcing
Limites :
- Peut alourdir les petits projets
- Demande une vraie discipline d’équipe
DDD — le métier au centre
Type : Domain-centric
Parfait pour les domaines riches, évolutifs, critiques.
Le Domain-Driven Design n’est pas une architecture,
c’est une philosophie de conception.
On conçoit le logiciel autour du langage métier et de ses invariants.
/Domain
/Aggregates
/Order.cs
/Customer.cs
/ValueObjects
/DomainEvents
/Application
/Features (Command/Query)
Exemple :
public class Order : AggregateRoot
{
private readonly List<OrderLine> _lines = new();
public Order(Guid customerId)
{
CustomerId = customerId;
Status = OrderStatus.Created;
}
public void AddLine(Product product, int quantity)
{
if (Status != OrderStatus.Created)
throw new DomainException("Cannot add line to finalized order");
_lines.Add(new OrderLine(product.Id, quantity));
AddDomainEvent(new OrderLineAddedEvent(Id, product.Id));
}
}
Model DDD (simplifié)

Avantages :
- Le code parle le langage du métier
- Règles, invariants et comportements explicites
- Se combine parfaitement avec CQRS et Hexagonale
Limites :
- Complexe à mettre en place pour un simple CRUD
- Nécessite une forte collaboration avec le métier
Architecture Hexagonale — Ports & Adapters
Type : Isolation-centric
Idéal pour les systèmes durables, modulaires, testables.
Aussi appelée Ports and Adapters :
le cœur métier est isolé de tout ce qui est technique (API, DB, UI…).
/Domain
/Application
/Infrastructure
/Adapters
/Database
/Messaging
/Api
Exemple :
// Port
public interface IOrderRepository
{
Task AddAsync(Order order);
}
// Adapter (EF Core)
public class EfOrderRepository : IOrderRepository
{
private readonly AppDbContext _db;
public Task AddAsync(Order order) => _db.Orders.AddAsync(order);
}
Avantages :
- Domaine indépendant des frameworks
- Testabilité maximale
- Évolutivité naturelle : remplacer EF, Kafka, etc.
Schéma Hexagonal (Ports & Adapters)

Comment tout ça s’articule
Ces patterns ne s’opposent pas, ils se superposent :
| Niveau | Pattern | Objectif |
|---|---|---|
| Modélisation | DDD | Refléter le métier |
| Organisation du code | Vertical Slice | Structurer par use case |
| Flux de données | CQRS | Séparer Command/Query |
| Structure logicielle | Hexagonale | Isoler le domaine des détails techniques |
Trouver le bon équilibre architectural
Chaque architecture répond à une intention, un contexte, un degré de maturité.
Choisissez la combinaison la plus juste selon la nature de votre projet :
- Pour un CRUD simple, une approche légère en Vertical Slice associée à un CQRS minimal suffit amplement : clarté, rapidité, efficacité.
- Pour un domaine métier complexe, privilégiez une base solide en DDD, enrichie du couple CQRS + Hexagonale pour préserver la cohérence et la testabilité du cœur métier.
- Pour un produit SaaS conçu pour durer, misez sur la durabilité : DDD pour le sens, Hexagonale pour l’isolation, Vertical Slice pour la modularité et l’évolution continue.
- Pour un MVP ou prototype rapide, commencez simplement par une architecture Vertical Slice. Vous pourrez ensuite la faire évoluer naturellement vers un modèle DDD à mesure que la complexité fonctionnelle se révélera.
En somme, chaque projet mérite son dosage : l’architecture n’est pas un dogme, mais une stratégie au service de la clarté et de la longévité du code.
Conclusion
Les développeurs ne devraient plus dire :
“On fait du DDD.”
ou
“Notre projet est CQRS.”
Mais plutôt :
“On a un domaine riche (DDD), organisé par slice (Vertical Slice),
avec une séparation lecture/écriture (CQRS)
et une isolation du cœur métier (Hexagonale).”
Ces approches ne s’excluent pas.
Elles forment un écosystème cohérent pour construire des applications vivantes, testables et durables.




