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

12 novembre 2025

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

ConceptTypeObjectif principal
DDD (Domain-Driven Design)Méthodologie / modélisationRefléter la réalité métier dans le code
Vertical Slice ArchitectureOrganisation du codeStructurer par fonctionnalité (use case) plutôt que par couche
CQRSPattern d’exécutionSéparer lecture et écriture
Hexagonale (Ports & Adapters)Style d’architectureIsoler 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 :

NiveauPatternObjectif
ModélisationDDDRefléter le métier
Organisation du codeVertical SliceStructurer par use case
Flux de donnéesCQRSSéparer Command/Query
Structure logicielleHexagonaleIsoler 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.