Oyun Kitabı
CQRS (Command Query Responsibility Segregation) dans les systèmes distribués : événement, courtier et projection (CQRS Command Query Responsibility Segregation Dans Les Systemes Distribues Evenement Courtier Et Projection)
Comment fonctionne CQRS dans les systèmes distribués ? Examinez les événements de domaine, le courtier de messages, la projection, la boîte d'envoi, l'idempotence et les éventuelles décisions de cohérence de bout en bout.
CQRS (Command Query Responsibility Segregation) - Anatomie des décisions
Partie 3 de 4
The problem we are solving first
An order has been created. Inventory must know, notifications must send an email, analytics must prepare a report, and search must index the new order.
Order Service
→ HTTP call for Inventory
→ HTTP call for Notifications
→ HTTP call for Analytics
→ HTTP call for Search
Should the Order Service call each of them over HTTP? Every new capability expands the call chain, failure surface, and pressure to deploy together. No. The Order Service should share the business fact that happened; how it is used belongs to each consumer.
Dans les deux premières parties de CQRS, nous avons séparé le déroulement d'une requête du côté commande et du côté requête. La nouvelle question dans un système distribué est la suivante : une fois la commande acceptée, comment les équipes d'inventaire, de notification, d'analyse et de recherche sont-elles informées en toute sécurité de ce changement ?text POST /orders → Command Handler → OrderPlaced olayı → Outbox → Relay → Message Broker ├─ Inventory projection ├─ Notification consumer ├─ Analytics projection └─ Search read model L'événement est un fait commercial réel et immuable ; OrderPlaced n'est pas une intention, c'est un fait qui s'est produit. Courtier déplace l'événement du producteur au consommateur. Consommateur traite l'événement sous sa propre responsabilité. Projection produit une vue adaptée à la lecture à partir du flux d'événements.
Concepts à la première mention```text
📦 Domain event Geçmiş zamanda ifade edilen, iş alanında gerçekleşmiş değişmez bir gerçektir.
📦 Broker Mesajı kalıcılaştıran ve tüketicilere dağıtan iletişim katmanıdır.
📦 Projection Event'lerden belirli bir ekran veya sorgu için üretilen read modeldir.
📦 Idempotency
Aynı event iki kez gelirse sonucu ikinci kez değiştirmeme özelliğidir.
```Une commande indique PlaceOrder ; Il demande un comportement au système. En cas de succès, OrderPlaced peut être publié. La commande peut être rejetée ; L'événement est un fait que vous ne pouvez plus changer.
La livraison par le courtier s'effectue souvent au moins une fois : le même message peut réapparaître. Le consommateur doit donc gérer les doublons en toute sécurité en masquant l'ID du message ou en concevant la transaction pour qu'elle soit intrinsèquement idempotente.```text if processedEvents.contains(event.id): return
applyProjection(event) markProcessed(event.id)
## Why the design looks this way
~~~text
PlaceOrder → an intent.
OrderPlaced → a fact that happened.
~~~
We make this distinction **because** a command can fail, while an event is a completed fact other systems can safely react to.
- **Outbox** is needed **because** the broker and database do not share one ACID transaction.
- A **projection** is needed **because** an admin panel, mobile app, dashboard, and analytics need the same order in different shapes without loading the aggregate.
- **Idempotency** is needed **because** a broker can redeliver the same message for reliable delivery.
- A **saga** is needed **because** inventory must not remain reserved forever when payment fails in e-commerce.
- Most applications that use CQRS do not use Event Sourcing; they are independent decisions.
## Qu'est-ce qui change lorsque la limite de service unique est dépassée ?
Dans une seule application, les transactions, l'enregistrement des données et les effets secondaires peuvent être gérés dans le même processus. Lorsque le système est distribué, l'inventaire, le courrier électronique et les rapports ont leurs propres cycles de vie. Il semble facile à court terme pour un service d’effectuer un appel HTTP synchrone vers un autre service ; Mais chaque appel ajoute de la latence, de la propagation des erreurs et une pression de co-déploiement.
Le flux basé sur les événements déplace cette dépendance vers le contrat de données. Le service de commande est responsable du schéma de l'événement `OrderPlaced` ; Le service stock consomme uniquement la pièce dont il a besoin. Le producteur ne connaît pas la base de données ou le runtime du consommateur. La déployabilité indépendante est simplement un déplacement de risque sans observabilité indépendante.
## Supprimer en toute sécurité l'événement de la limite de la transaction
Le flux naïf est :```text
Save order
→ Commit
→ Publish OrderPlaced
```Si la validation réussit mais que la publication échoue, le système connaît l'ordre, les autres services ne le savent pas. Le fait de ne pas publier et valider dans l'ordre inverse produit également un événement fantôme. Le **modèle de boîte d'envoi** nécessite deux écritures dans la même transaction locale :```text
Transaction
→ Order kaydını yaz
→ Outbox'a OrderPlaced kaydını yaz
→ Commit
Relay
→ Outbox kaydını broker'a ilet
→ teslim edildi olarak işaretle
```La boîte d'envoi n'établit pas de transactions distribuées ; Il sécurise le fait critique : s'il y a une commande dans la base de données, il y a un événement à publier. Le relais peut réessayer. Le besoin d’idempotence du côté des consommateurs ne disparaît donc pas.
**CDC** peut capturer les modifications du journal de la base de données. CDC est puissant pour propager les changements dans une base de données existante ; Cependant, vous pouvez avoir un contrôle limité sur la langue du domaine. La boîte d'envoi, en revanche, permet à l'application de sélectionner clairement quel événement a une signification commerciale.
| Question | CDC | Boîte d'envoi |
| --- | --- | --- |
| Sélection d'un événement par langue de domaine | Limité | Clair et complet |
| Lien vers la transaction de candidature | Indirect | Direct |
| Besoin d'idempotence du consommateur | Oui | Oui |
## Projection : vue orientée intention, pas une copie
Le modèle de lecture n'est pas une copie incomplète du modèle d'écriture. `ProductSearchRow` peut être généré pour une liste de produits et `OrderFulfilmentSummary` peut être généré pour le panneau de commande. Un même événement peut alimenter différentes projections de différentes équipes.```text
OrderPlaced
→ OrderSummaryProjection
→ { orderId, customerName, total, status }
OrderPlaced
→ InventoryProjection
→ { sku, reservedQuantity, availability }
```Les projections doivent être reconstructibles. Lorsque le code change ou que l'erreur est corrigée, le flux d'événements est relu de manière contrôlée et le nouveau modèle de lecture est vérifié. Pour cela, l’ordre des événements, le point de contrôle, la gestion des versions et la vitesse de relecture doivent être mesurés. Le délai de projection doit être visible dans le langage du produit : est-il acceptable qu'il apparaisse dans la liste quelques secondes après que l'utilisateur a passé commande ? La réponse est une décision commerciale et non technique.
## Choisir un courtier n'est pas un choix de marque
Courtier ; Il offre des garanties de tri, de persistance, de groupe de consommateurs, de relecture et de file d'attente d'erreurs. Si la commande est importante pour une même clé, une clé de partition doit être conçue. Si le consommateur peut être à la traîne, des stratégies de retard, de réessai et de lettre morte doivent être suivies. Si le schéma change, ajoutez un nouveau champ, ne supprimez pas l'ancien champ à la hâte ; La version de l'événement doit rester rétrocompatible.
## Event Sourcing et CQRS ne sont pas la même chose
Le CQRS sépare les responsabilités de lecture et d’écriture. Event Sourcing, en revanche, produit l'état agrégé à partir du flux d'événements ajouté uniquement au lieu de la ligne actuelle. Ils peuvent être utilisés ensemble ; Cependant, Event Sourcing n’est pas obligatoire pour CQRS et Kafka n’est pas obligatoire pour Event Sourcing. Lorsque Event Sourcing est sélectionné, l'instantané, la version du flux et le coût de relecture sont conçus séparément.
## Saga : décision de rémunération dans un workflow distribué
Une commande peut passer par des étapes de paiement, de stock et d'expédition ; Ces étapes ne peuvent pas tenir dans une seule transaction ACID. Saga définit une étape de compensation pour chaque opération locale.```text
Order placed
→ reserve inventory
→ capture payment
→ create shipment
payment fails
→ release inventory
→ mark order failed
```**Chorégraphie** permet aux services de se déclencher mutuellement avec des événements ; La dépendance locale est faible, mais il devient difficile de suivre l'ensemble des flux. **L'orchestration** rend le flux visible avec le gestionnaire de processus central ; à son tour, cela ajoute une responsabilité de coordination.
## Liste de contrôle opérationnel
1. Chaque événement est-il nommé au passé et dans le langage des affaires ?
2. La boîte d'envoi comble-t-elle le fossé entre l'enregistrement dans la base de données et la publication d'événements ?
3. Est-il sans danger pour les doublons de consommateurs, la corruption de files d'attente et les événements tardifs ?
4. Le point de contrôle de projection, le décalage et la procédure de reconstruction peuvent-ils être respectés ?
5. Le propriétaire du contrat d'événement, la stratégie de publication et la règle de compatibilité ascendante sont-ils clairs ?
6. L'éventuelle fenêtre de cohérence visible par l'utilisateur est-elle acceptée par le produit ?
Si ces questions ne trouvent pas de réponse, le système apparaît piloté par les événements ; mais il agit en fonction de l'échec.
## Distinctions souvent confuses```text
❌ Event = Command
✓ Command niyettir; event gerçekleşmiş gerçektir.
❌ Broker = Event Store
✓ Broker event'i taşır; Event Store domain geçmişinin kalıcı kaynağı olabilir.
❌ Projection = cache
✓ Cache hız için geçicidir; projection iş sorgusu için bilinçli bir read modeldir.
❌ At-least-once = hata
✓ Tekrar teslimat normaldir; consumer idempotent olmalıdır.
```## Véritable streaming de bout en bout```text
POST /orders
→ Controller
→ Mediator.Send()
→ Validation + Authorization
→ Transaction Behavior
→ PlaceOrderHandler
→ Order aggregate
→ Order + Outbox commit
→ Relay
→ Broker
→ Projection consumer
→ Read database
→ GET /orders
→ Query Handler
→ OrderSummaryDto
→ Frontend
Quand dois-je utiliser ce modèle ?
Commencez par le CQRS logique : nommez les commandes en langage métier, réduisez les requêtes à ce dont l'écran a besoin et clarifiez les limites de la transaction. Les courtiers et les projections séparées ne doivent être ajoutés que lorsque des consommateurs indépendants, une charge de lecture asymétrique ou la nécessité d'une intégration rejouable sont prouvés.
Bien que le coût de chaque nouveau consommateur puisse ressembler à un changement de code O(1), le coût opérationnel n'est pas fixe : un contrat, un tableau de bord, une alarme, une nouvelle tentative, une propriété et un scénario de test sont requis.
Réflexion
La promesse du CQRS distribué n’est pas davantage de messages, mais des responsabilités plus visibles. Les événements portent l'historique, les courtiers portent le flux et les projections portent le résultat visible pour l'utilisateur.
Un flux d'événements ne devient une décision architecturale que s'il est sûr lorsqu'il arrive à nouveau, arrive en retard et est rejoué.
Dans la section suivante, nous examinerons ce qui change lorsqu'un système tombe en panne pendant son exécution : cohérence, doublons, projections corrompues et stratégies de récupération.
What should remain with you?
If you remember only five things:
- A command requests behavior.
- An event is a fact that happened.
- A broker carries events to the right consumers.
- A projection reads the same fact in the shape each screen needs.
- Outbox prevents event loss because the broker and database do not share one ACID transaction.
Every separation in this chapter has a reason: idempotency is needed because a broker can redeliver; a projection is needed because querying an aggregate for every screen is expensive and the wrong abstraction.
FAQ
Frequently asked questions
Qu'est-ce qu'un événement de domaine ?
Exprimé au passé, il s’agit d’un fait immuable qui s’est produit dans le domaine des affaires.
Qu'est-ce qu'un courtier ?
C'est la couche de communication qui perpétue le message et le distribue aux consommateurs.
"Event=Command" est-il correct ?
Le commandement est l’intention ; L'événement est réel.
Principes d'ingénierie appris
- Dans un système distribué, un événement n'est pas une commande ; C’est un fait économique établi et immuable.
- L’équivalent d’une livraison au moins une fois est un consommateur idempotent ; le message en double est une entrée de conception, pas une exception.
- La projection doit être reconstructible, mesurable, et son délai doit être défini dans le langage du produit.
Continuer la lecture
Continuer la lecture
Suivant en série
CQRS (Command Query Responsibility Segregation) en production : cohérence, erreurs et stratégies de récupération
Comment CQRS fonctionne-t-il en toute sécurité dans un environnement de production ? Décalage de cohérence, événement en double, corruption de séquence,…
Suivant en série
Comment fonctionne le pipeline CQRS (Command Query Responsibility Segregation) ? Anatomie du flux de commande et de requête
Qu’est-ce que le pipeline de requêtes CQRS ? Comment une requête HTTP se déroule-t-elle via Controller, MediatR, le comportement du pipeline, le…
Même série
De CRUD (Créer, Lire, Mettre à jour, Supprimer) à CQRS (Command Query Responsibility Segregation) : le problème est le modèle, pas le code
Qu'est-ce que CQRS, quelle est la différence entre CRUD et CQRS et quand faut-il utiliser CQRS ? Un guide expliquant pourquoi un seul modèle ne suffit pas…