CQRS et Event Sourcing en Rust : Architecture pour une performance sans compromis

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

Pourquoi CQRS et Event Sourcing ne sont pas du « sur-engineering », mais un salut pour les systèmes complexes

\n\n

Avez-vous déjà été confronté à une situation où votre système, écrit en Rust, gère parfaitement les requêtes de lecture, mais « s'effondre » lors de l'écriture ? Ou inversement — vous sacrifiez les performances de lecture pour pouvoir conserver l'historique complet des modifications ? Cela vous parle ? Alors bienvenue dans le monde du CQRS et de l'Event Sourcing.

\n\n

De nombreux développeurs considèrent ces patrons comme de la « grosse artillerie », applicable uniquement dans les projets d'entreprise en C# ou Java. Mais avec la popularité croissante de Rust dans les systèmes à forte charge (des serveurs de jeux aux plateformes financières), CQRS et Event Sourcing deviennent non seulement utiles — ils deviennent indispensables. Rust, avec son coût zéro des abstractions et son typage strict, est le langage idéal pour implémenter ces patrons.

\n\n

Dans cet article, nous ne nous contenterons pas de parler de théorie. Nous écrirons du code fonctionnel en Rust que vous pourrez adapter à vos projets. Vous apprendrez à séparer les responsabilités entre les commandes et les requêtes, à stocker des événements plutôt que l'état actuel, et comment tout cela accélère considérablement votre système.

\n\n

Qu'est-ce que CQRS et Event Sourcing ? En termes simples pour un développeur Rust

\n\n

Imaginez que vous gérez un compte bancaire. Dans un modèle CRUD traditionnel, vous avez une table « Comptes » qui stocke le solde actuel. Si vous devez consulter l'historique, vous créez une autre table « Transactions ». Mais que faire si vous avez besoin non seulement de connaître le solde, mais de comprendre pourquoi il a changé ? Ou de restaurer l'état à n'importe quelle date ?

\n\n

CQRS : Diviser pour mieux régner

\n\n

CQRS (Command Query Responsibility Segregation) est un principe qui sépare les opérations en deux groupes :

\n
    \n
  • Commandes (Commands) — modifient l'état. Elles ne renvoient pas de données, seulement une confirmation ou une erreur.
  • \n
  • Requêtes (Queries) — lisent uniquement les données. Elles ne modifient pas l'état.
  • \n
\n\n

En Rust, cette séparation semble naturelle : les types de commandes et de requêtes sont des structures différentes, et leurs gestionnaires sont des fonctions ou des modules distincts. Ce n'est pas simplement une « bonne pratique », c'est un moyen d'éviter les mutations accidentelles lors de la lecture.

\n\n
// Commande — modifie l'état\npub struct CreateOrder {\n    pub user_id: Uuid,\n    pub items: Vec<OrderItem>,\n}\n\n// Requête — lit uniquement\npub struct GetOrderHistory {\n    pub user_id: Uuid,\n    pub limit: usize,\n}
\n\n

Event Sourcing : Stockez l'historique, pas un instantané

\n\n

Event Sourcing est une approche où nous ne sauvegardons pas l'état actuel d'un objet, mais la séquence d'événements qui y a conduit. Vous voulez connaître le solde d'un compte au 1er janvier 2023 ? Il suffit de « rejouer » tous les événements depuis le début jusqu'à cette date.

\n\n

Les avantages sont évidents :

\n
    \n
  • Audit — chaque modification est enregistrée.
  • \n
  • Instantanés temporels — vous pouvez restaurer n'importe quel état.
  • \n
  • Résistance aux bogues — si le code contient une erreur, vous n'avez pas perdu les données, vous les avez simplement mal « rejouées ».
  • \n
\n\n

Exemple pratique : Système de gestion de commandes en Rust

\n\n

Construisons un mini-système pour une boutique en ligne. Il traitera les commandes (création de commande, ajout d'article) et les requêtes (obtention du panier, historique des commandes). Toute la logique sera basée sur des événements.

\n\n

Étape 1 : Définir les événements et les commandes

\n\n

La première chose à faire est de concevoir le domaine. En Rust, nous utilisons des énumérations (enum) pour les événements et des structures strictes pour les commandes.

\n\n
use serde::{Deserialize, Serialize};\nuse uuid::Uuid;\n\n// Événements — faits immuables\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub enum OrderEvent {\n    Created { order_id: Uuid, user_id: Uuid, timestamp: i64 },\n    ItemAdded { order_id: Uuid, item: Str                

Blogs

Book Recommendations