CQRS et Event Sourcing en C# : Architecture pour les systèmes complexes

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Quand le CRUD classique ne suffit plus : Introduction à CQRS et Event Sourcing

\n\n

Imaginez que vous développez un système bancaire. Chaque seconde, des centaines d'utilisateurs consultent leur solde, transfèrent de l'argent, ouvrent des comptes. Votre application en C# fonctionne avec un Entity Framework standard et des opérations CRUD typiques. Et soudain — la base de données plante. Pourquoi ? Parce qu'un rapport lourd a bloqué une table pendant l'écriture d'une transaction. Cela vous dit quelque chose ?

\n\n

Les développeurs de systèmes complexes en C# finissent tôt ou tard par atteindre les limites de l'architecture classique. Deux puissants patterns viennent à la rescousse : CQRS (Command Query Responsibility Segregation) et Event Sourcing. Analysons-les en pratique — avec des exemples concrets, pas de théorie sèche.

\n\n

Qu'est-ce que CQRS et pourquoi ce n'est pas "juste une séparation" ?

\n\n

CQRS ne consiste pas à ranger les méthodes dans des dossiers différents. C'est une philosophie : les commandes (modifient l'état) et les requêtes (lisent l'état) doivent vivre séparément. Dans le CRUD classique, vous avez un seul modèle de données pour la lecture et l'écriture. En CQRS, vous en avez au moins deux.

\n\n

Le problème de l'approche classique

\n

Supposons que vous ayez un UserRepository avec une méthode GetUserById. Lorsque vous lisez un utilisateur pour l'afficher à l'écran, vous avez besoin de 3 champs. Lorsque vous le mettez à jour, vous avez besoin de 10 champs et de validations. Résultat : un seul modèle se transforme en monstre avec des champs null et des mappers complexes.

\n\n

Avec CQRS, vous séparez les responsabilités :

\n
    \n
  • Command — exécute une action (ex : CreateOrderCommand)
  • \n
  • Query — retourne des données (ex : GetOrderQuery)
  • \n
  • Read Model — DTO plat, optimisé pour un écran spécifique
  • \n
  • Write Model — modèle domaine riche avec logique métier
  • \n
\n\n

Écrire du CQRS en C# avec MediatR

\n

La façon la plus populaire d'implémenter CQRS en C# est d'utiliser la bibliothèque MediatR. Elle implémente le pattern Mediator et s'adapte parfaitement au concept de commandes et de requêtes.

\n\n

Étape 1 : Définir une commande

\n
public record CreateOrderCommand(\n    Guid CustomerId,\n    List<OrderItemDto> Items,\n    string ShippingAddress\n) : IRequest<Guid>;  // Retourne l'ID de la commande créée
\n\n

Étape 2 : Gestionnaire de commande

\n
public class CreateOrderCommandHandler : IRequestHandler<CreateOrderCommand, Guid>\n{\n    private readonly IOrderRepository _orderRepository;\n    private readonly IUnitOfWork _unitOfWork;\n\n    public CreateOrderCommandHandler(IOrderRepository orderRepository, IUnitOfWork unitOfWork)\n    {\n        _orderRepository = orderRepository;\n        _unitOfWork = unitOfWork;\n    }\n\n    public async Task<Guid> Handle(CreateOrderCommand request, CancellationToken cancellationToken)\n    {\n        // Logique métier : vérification, validation, création\n        var order = new Order(request.CustomerId, request.ShippingAddress);\n        \n        foreach (var item in request.Items)\n        {\n            order.AddItem(item.ProductId, item.Quantity, item.Price);\n        }\n\n        await _orderRepository.AddAsync(order);\n        await _unitOfWork.SaveChangesAsync(cancellationToken);\n        \n        return order.Id;\n    }\n}
\n\n

Étape 3 : Définir une requête

\n
public record GetOrderSummaryQuery(Guid OrderId) : IRequest<OrderSummaryDto>;
\n\n

Étape 4 : Gestionnaire de requête (lecture depuis une BDD séparée !)

\n
public class GetOrderSummaryQueryHandler : IRequestHandler<GetOrderSummaryQuery, OrderSummaryDto>\n{\n    private readonly IReadDbContext _readDbContext; // Contexte séparé pour la lecture\n\n    public GetOrderSummaryQueryHandler(IReadDbContext read                

Blogs

Book Recommendations