CQRS et Event Sourcing en Java : Une architecture qui n'a pas peur de la complexité

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

Introduction : Quand le CRUD ne suffit plus

\n\n

Imaginez que vous développez un système pour les échanges boursiers. Chaque seconde, des milliers d'ordres. Chaque ordre n'est pas simplement "insérer un enregistrement". C'est une vérification de solde, un recalcul de portefeuille, un audit, des notifications. Imaginez maintenant que vous deviez non seulement enregistrer l'état actuel, mais aussi reconstituer ce qui s'est passé à 14:35:22.003 hier. Le CRUD (Create, Read, Update, Delete) classique échoue ici. Bienvenue dans le monde du CQRS et de l'Event Sourcing en Java.

\n\n

Ces deux patrons ne sont pas de simples mots à la mode. Ce sont des outils qui permettent de construire des systèmes à haute performance, avec un audit complet et la possibilité de "remonter le temps". Dans cet article, nous allons voir comment implémenter le CQRS (Command Query Responsibility Segregation) et l'Event Sourcing en Java, en utilisant des frameworks modernes et du code propre. Sans blabla, avec des exemples.

\n\n

Qu'est-ce que le CQRS en termes simples

\n\n

CQRS est la séparation des responsabilités entre les commandes (écriture) et les requêtes (lecture). Dans le CRUD classique, vous utilisez le même modèle pour la lecture et l'écriture. En CQRS, vous créez deux modèles distincts : un pour modifier les données (commandes), un autre pour les extraire (requêtes).

\n\n

Pourquoi est-ce nécessaire ?

\n
    \n
  • Performance : La lecture et l'écriture évoluent indépendamment. Vous pouvez avoir 10 réplicas pour la lecture et 2 pour l'écriture.
  • \n
  • Sécurité : Les commandes peuvent vérifier les règles métier, tandis que les requêtes se contentent de fournir les données.
  • \n
  • Flexibilité : Pour la lecture, vous pouvez utiliser des vues dénormalisées, pour l'écriture, des vues normalisées.
  • \n
\n\n

Exemple de code : Séparation des commandes et des requêtes

\n\n

Supposons que nous ayons un système de gestion des commandes. Au lieu de faire OrderService.save(order), nous séparons :

\n\n
// La commande est une intention de modifier l'état\npublic class CreateOrderCommand {\n    private final String orderId;\n    private final String customerId;\n    private final List<OrderItem> items;\n\n    // constructeur, getters...\n}\n\n// Gestionnaire de commande\n@Component\npublic class CreateOrderCommandHandler {\n    @Autowired\n    private OrderRepository orderRepository;\n\n    public void handle(CreateOrderCommand command) {\n        // Validation des règles métier\n        if (command.getItems().isEmpty()) {\n            throw new IllegalArgumentException("La commande doit contenir au moins un article");\n        }\n        Order order = new Order(command.getOrderId(), command.getCustomerId(), command.getItems());\n        orderRepository.save(order);\n    }\n}\n\n// La requête est une récupération de données\npublic class GetOrderQuery {\n    private final String orderId;\n    // constructeur, getters...\n}\n\n// Gestionnaire de requête\n@Component\npublic class GetOrderQueryHandler {\n    @Autowired\n    private OrderReadRepository orderReadRepository;\n\n    public OrderDTO handle(GetOrderQuery query) {\n        // Lecture directe depuis une base de données optimisée\n        return orderReadRepository.findById(query.getOrderId());\n    }\n}
\n\n

Remarquez : OrderRepository (écriture) et OrderReadRepository (lecture) sont des interfaces différentes. Elles peuvent pointer vers des bases de données ou des schémas différents.

\n\n

Event Sourcing : Stocker non pas l'état, mais l'historique

\n\n

Event Sourcing est un patron où nous sauvegardons non pas l'état actuel d'un objet, mais la séquence d'événements qui ont conduit à cet état. Au lieu de "Commande n°123 : statut = LIVRÉE", nous stockons : "Événement 1 : Commande créée", "Événement 2 : Article ajouté", "Événement 3 : Commande payée", "Événement 4 : Commande livrée".

\n\n

Comment cela fonctionne en Java

\n\n

Chaque événement est un objet immuable (immutable). Nous les sauvegardons dans un Event Store (base de données spécialisée). L'état actuel de l'agrégat est calculé en rejouant (replaying) tous les événements.

\n\n
// Événement de base\npublic interface Do                

Blogs

Book Recommendations