DDD, Vertical Slice, CQRS, Hexagonale : les devs confondent tout — mettons de l’ordre

pattern_concepts

Le grand flou architecturalDans 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

Le **Layered Crud**, pertinent pour les projets **data-centric**.**Diagramme — Architecture en couches**![](https://xavierraboteur.wordpress.com/wp-content/uploads/2025/11/diagramme_arch_couches.png)## 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**![](https://xavierraboteur.wordpress.com/wp-content/uploads/2025/11/untitled-diagram-2025-11-12-122613.png)*voir l'article => [https://xavierraboteur.fr/2025/10/14/vertical-slice-modular-monolith-le-sweet-spot/](/articles/vertical-slice-modular-monolith-le-sweet-spot)*## CQRS — Command Query Responsibility SegregationType : **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**![](https://xavierraboteur.wordpress.com/wp-content/uploads/2025/11/untitled-diagram-2025-11-12-122958.png)**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 centreType : **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é)**![](https://xavierraboteur.wordpress.com/wp-content/uploads/2025/11/untitled-diagram-2025-11-12-123231.png)**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 & AdaptersType : **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)**![](https://xavierraboteur.wordpress.com/wp-content/uploads/2025/11/untitled-diagram-2025-11-12-123503.png)## Comment tout ça s’articuleCes 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 architecturalChaque 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.## ConclusionLes 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**.
Xavier Raboteur