
J’utilise Claude Code à diverses fins, et j’ai réalisé que plus il comprend les fonctionnalités du système (le domaine, les cas d’utilisation, les flux de bout en bout), plus il peut m’aider.
Par exemple, lorsque je colle un journal d’erreurs de production, Claude peut lire la trace de la pile, identifier le code concerné et me dire s’il y a un bug. Mais lorsque le problème est plus complexe, comme un ticket de support client, et qu’il n’y a pas de trace de pile, Claude est moins utile.
Le principal défi réside dans le fait que les processus de bout en bout sont longs et complexes, s’étendant sur de nombreux référentiels de code. Donc, demander simplement à Claude Code d’analyser un seul référentiel n’allait pas fonctionner (et le /init par défaut ne produisait pas suffisamment de détails, même pour cette seule base de code).
J’ai donc décidé d’utiliser Claude Code pour analyser le système afin de cartographier les flux de bout en bout pertinents pour le domaine dans lequel je travaille afin que Claude Code (et les humains) puissent l’utiliser pour gérer des défis plus complexes.
Cet article partage ce que j’ai réalisé en une journée, en m’appuyant sur les connaissances et les outils que j’ai déjà acquis à partir d’exemples de travail et d’expériences réels.
Ceci est un article dans une série. Vous pouvez retrouver les autres articles ici.
Cet article a été écrit à 100% par moi. J’ai demandé à Claude de générer l’exemple anonymisé à la fin reflétant le type de contenu et le style utilisés dans les exemples réels que j’ai créés.
Définir le contexte initial
Pour commencer mon projet, j’ai créé un document d’exigences très léger :
# AI Architecture Analysis
This document contains the instructions for an important task - using AI to define the architecture of this system, so that it can be used by humans and AI agents to more easily understand the system.
## Objective
Map out all of the flows that this application is involved in (use sub agents where necessary to work in parallel). A flow should map out the end-to-process from an action in the UI (in the (readacted) repo) to a BFF, to backend APIs, or flows that are triggered by events.
Flows should be documented in Mermaid format to allow AI agents to understand, for versioning (in git), and for easy visualization.
## Requirements
Each flow should have a descriptive name and should include:
1. The URL path of the page where the interaction is triggered
2. The URL path of the BFF endpoint (and the repository it lives in)
3. The URL path of calls made to downstream services
4. Any database interactions
5. Any events produced or consumed (full name of event e.g. orders.orderPlaced)
6. Consumers of events (if easy to identify)
7. Any workflows triggered (like the synchronizeOrder)
To do this, you will need to look in other repositories, which can be found in the parent folder. The GitHub client can also be used if necessary.
The list of flows should live in ../flows/index.md and each individual flow should be defined in a separate folder.
# Where to find information
- /docs/architecture contains various folders describing the design of this system and domain knowledge
- Each API project in this repository ((redacted), (redacted)) has an openapi.json. This must be used to identify all flows and validate. The (redacted) and (redacted) repositories also have openapi spec files
- The entities in the domain (redacted), (redacted) (redacted) have method that clearly describe the domain operations that can be performed on them. Equally, each operation is invoke from a use case that clearly describes the use case
Le résultat que je souhaite est constitué de flux de bout en bout tels que :
UI -> BFF -> API -> mise à jour de la base de données -> événement de publication -> gestionnaire -> cas d’utilisation -> événement de publication ->…
Je ne veux pas de 10 types différents de diagrammes d’architecture et de différents niveaux de détail. Je souhaite que Claude Code comprenne le comportement du système afin qu’il puisse identifier les anomalies (en examinant les données de production et les journaux) et analyser l’impact des changements potentiels.
J’ai également créé quelques informations légères sur le système dans ces deux fichiers :

Le fichier de concepts de domaine explique les entités du système. Explication très brève. Le fichier de présentation du système explique la relation entre cette base de code et d’autres référentiels, ce qui est crucial. Encore une fois, c’est très léger : une liste à puces de noms de référentiels et une ou deux phrases décrivant leur relation avec celui-ci.
Recherche dans plusieurs référentiels
Les instructions pour cette tâche se trouvent dans le référentiel principal du domaine dans lequel je travaille. C’est le centre de l’univers de cet agent, mais il doit être capable de lire d’autres référentiels pour rejoindre le flux de bout en bout.
La solution que j’utilise pour cela est dans la description ci-dessus :
Pour ce faire, vous devrez chercher dans d’autres référentiels qui se trouvent dans le dossier parent. Le client GitHub peut également être utilisé si nécessaire.
J’accorde à Claude les autorisations suivantes dans .claude/settings.local.json et il peut ensuite accéder à tous les référentiels sur la machine ou utiliser le client GitHub s’il pense qu’il existe des référentiels que je n’ai pas disponibles localement :
"permissions": {
"allow": (
...
"Read(//Users/nicktune/code/**)",
...
Dire à Claude où chercher
Vous remarquerez que les exigences donnent également à Claude des conseils sur où rechercher des informations clés telles que les fichiers de spécifications Open API, qui sont comme un index des opérations prises en charge par l’application.
Ceci est utile comme mécanisme de validation plus tard dans le flux. Je demanderais à Claude : « Répertoriez tous les points de terminaison et événements d’API produits ou consommés par cette application : y en a-t-il qui ne font partie d’aucun flux. » Je peux alors voir si nous avons manqué quelque chose d’important.
Cartographie du premier flux
J’ai mis Claude en mode plan et lui ai demandé de lire le fichier. Un court questionnaire est ensuite apparu me demandant quels étaient mes besoins et mes préférences. L’une des questions posées concernait le processus : devrions-nous cartographier l’ensemble du système en parallèle, travailler étape par étape, etc. ?
J’ai dit : faisons le premier ensemble et utilisons-le comme modèle pour les autres.
Il a fallu environ deux heures pour créer le premier flux, pendant que j’examinais ce que Claude avait produit et que je donnais mon avis sur ce dont j’avais besoin. Par exemple, au début, il a créé un diagramme de séquence qui avait l’air joli mais qui était trop difficile à lire pour les flux complexes impliquant de nombreux référentiels.
Finalement, nous avons opté pour des diagrammes de flux horizontaux dans lesquels chaque référentiel est un conteneur et nous avons défini quelles pourraient être les étapes. Au début, les étapes étaient trop granulaires et ajoutaient des appels de méthodes individuels.
### diagram.mermaid Requirements
**CRITICAL RULES:**
1. **File Format**: Must be pure Mermaid syntax with `.mermaid` extension- NO markdown headers
- NO markdown code fences (no ` ```mermaid ` wrapper)
- Starts directly with `flowchart LR`
2. **Use Swimlane Format**: `flowchart LR` (left-to-right with horizontal swimlanes)
- Each repository is a horizontal swimlane (subgraph)
- Flow progresses left to right
- Swimlane labels should be prominent (use emoji for visibility)
- Example: `subgraph systemA("🔧 systemA")`
3. **Systems as Containers**:
- Each repository MUST be a `subgraph` (horizontal swimlane)
- Repository name is the subgraph label
- Operations are nodes inside the subgraph
- Use `direction LR` inside each subgraph
4. **Valid Step Types** - A step in the diagram can ONLY be one of the following:
- **HTTP Endpoint**: Full endpoint path (e.g., `POST /blah/{blahId}/subblah`)
- **Aggregate Method Call**: Domain method on an aggregate (e.g., `Order.place`, `Shipping.organiz`)
- **Database Operation**: Shown with cylinder shape `((Database: INSERT order))`
- **Event Publication**: (e.g., `Publish: private.ordering.order.placed`)
- **Workflow Trigger**: Must be labeled as workflow (e.g., `⚙️ Workflow: syncOrders`)
- **Workflow Step**: Any step inside a workflow MUST include the workflow name as prefix (e.g., `syncOrderWorkflow: Update legacy order`, `updateOrderInfo: POST /legacy/fill-order`)
- **Lambda Invocation**: (e.g., `Lambda: blah-blah-lambda-blah-blah`)
- **UI Actions**: User interactions (e.g., `Show modal form`, `User enters firstName, lastName`)
J’ai ajouté un flux anonymisé à la fin de ce document.
J’ai également dû ajouter quelques corrections à Claude pour m’assurer qu’il cherchait aux bons endroits et qu’il comprenait ce que certains concepts signifient dans d’autres parties de notre système ; nous ne nous sommes pas contentés de parcourir le diagramme pendant deux heures.
Choisir le prochain flux
Après le premier flux, les flux suivants se sont déroulés beaucoup plus rapidement. J’ai vérifié le résultat de chaque flux et donné un feedback à Claude, mais généralement autour de 15 minutes, et la plupart du temps ça fonctionnait donc je pouvais faire autre chose en attendant.
L’un des défis intéressants consistait à décider quels flux étaient réellement nécessaires ? Qu’est-ce qu’un flux ? Où doit commencer et se terminer un flux ? Qu’en est-il des relations entre les flux ?
Ici, j’étais un peu aux commandes. J’ai demandé à Claude de proposer des flux (il suffit de les lister avant d’analyser), puis je lui ai demandé de me montrer comment chaque point de terminaison et événement de l’API s’inscrivait dans les flux, et nous l’avons utilisé pour itérer un peu.
L’une des choses que j’ai dû faire après que Claude ait produit la première ébauche est de demander : « Êtes-vous sûr qu’il n’y a pas d’autres consommateurs pour ces événements qui ne sont pas répertoriés ici ? Il effectuait alors une recherche plus approfondie et trouvait parfois des consommateurs dans des référentiels que je n’avais pas localement. (Cela utiliserait la recherche GitHub.)
Valeur d’apprentissage
En examinant chaque cas d’utilisation, j’apprenais des choses sur le système que je ne comprenais pas entièrement ou peut-être qu’il y avait des nuances dont je n’étais pas conscient. Cela seul aurait justifié tous les efforts que j’y ai consacrés.
Ensuite, j’ai commencé à imaginer la valeur pour les personnes qui découvrent une base de code ou un système existant que personne ne comprend plus. Ou peut-être quelqu’un qui travaille dans une autre équipe et qui a besoin de comprendre comment un bug ou un changement dans son domaine est lié à d’autres domaines.
Faire évoluer les exigences
Au fur et à mesure du processus, je demandais régulièrement à Claude de mettre à jour le dossier des exigences. Ainsi, après avoir terminé le premier flux, des instructions comme celle-ci ont été ajoutées au fichier :
## Documentation Structure
Each flow must be documented in a separate folder with the following structure:
```
docs/architecture/flows/(flow-name)/
├── README.md # Complete documentation (all content in one file)
└── diagram.mermaid # Mermaid diagram
```
**IMPORTANT**: Use ONLY these two files. Do NOT create separate diagram-notes.md or other files. Keep all documentation consolidated in README.md for easier maintenance.
### README.md Contents
**Use the blueprint as a template**: `docs/architecture/flows/(redacted)/`
Le fichier compte désormais 449 lignes.
L’une des raisons pour lesquelles j’ai fait cela était pour pouvoir démarrer une nouvelle session Claude, maintenant ou dans le futur, sans fenêtre de contexte complètement propre et exécuter le processus pour obtenir des résultats similaires.
J’ai en fait utilisé une nouvelle session pour cartographier chaque nouveau flux afin de valider que les exigences étaient quelque peu reproductibles. En général, c’était le cas, mais Claude ignorait souvent certaines parties des exigences. Donc à la fin, je lui ai dit de revoir les exigences et de comparer les résultats, et il identifierait généralement la plupart des erreurs qu’il avait commises et les corrigerait.
Voici un exemple de certaines des règles qui ont commencé à se développer. Certains visaient à garantir que Claude produisait le bon type de sortie, et d’autres devaient aider Claude à éviter des erreurs courantes telles que les erreurs de syntaxe de Mermaid.
### 2. Trace Workflows to Their Final Event
**Problem**: Missing events because you don't read the actual workflow implementation.**Rule**: When you encounter a workflow, you MUST:
1. Find the workflow definition file (usually `.asl.json` for AWS Step Functions)
2. Read the ENTIRE workflow to see ALL events it publishes
3. Document EVERY event in sequence
**Example from our blueprint**:
- We initially thought `(readacted)` ended with `(readacted)`
- Reading `(redacted).asl.json` revealed it actually ends with `(readacted)`
- This event was CRITICAL to the flow continuing**File locations**:
- Integrations workflows: `(another-repo)/domains/*/workflows/*.asl.json`
- Look for `SendEvent` or `publish` actions in the workflow steps
Claude Code ne suit jamais toutes les instructions décrites. Mais l’ajout de ces règles semble augmenter la qualité et réduire le nombre d’itérations nécessaires.
Tester la sortie
Après avoir cartographié quatre ou cinq flux, j’ai décidé qu’il était temps de les tester : ces informations aident-elles réellement Claude Code, ou est-ce principalement un public humain qui bénéficie des résultats ?
Je suis donc allé dans notre système de tickets d’assistance et j’ai choisi un ticket qui semblait complexe. J’ai demandé à mon agent enquêteur d’examiner le ticket et d’identifier le problème. Mais cette fois, j’ai ajouté une étape supplémentaire dans ses instructions :
4. Identify the affected flows in /docs/architecture/flows
Dans le cadre de son analyse, il a déclaré :
Permettez-moi d’explorer les flux d’architecture pour comprendre comment (expurgé) et (expurgé) doivent être gérés :
Puis il a produit son analyse identifiant correctement le flux et les comportements attendus :
## Affected Flow
**(BlahA Upload and BlahB History Thing)(../architecture/flows/(redacted)/README.md)**
The (redacted) workflow should populate (redacted) data via:
- (redacted) selection ((redacted) standard OR (redacted) enhanced based on `(redacted)` flag)
- Parallel execution of: DomainA, DomainB, DomainC, DomainD
- Publishing `order.blahBlahed` event on completion
…
Et pour les étapes suivantes, il souhaitait interroger les événements publiés par le système pour vérifier que ce qui s’était réellement passé correspondait aux comportements attendus définis dans le flux :
### Step 1: Query (redacted) Events for Both (redacted)**Objective:** Compare event history to identify what happened differently between old and new (redacted)
**What to look for:**
- `(redacted event name)` - Did (redacted) complete for new (redacted)?
- `(redacted event name)` - Were (redacted) created?
- `(redacted event name)` - Were (redacted) created?
- `(redacted event name)` - Did workflow complete for new (redacted)?
- `(redacted event name)` - Final confirmation event
- Any error/failure events related to the workflow
Auparavant, Claude aurait dû analyser la base de code pour déterminer ce qui aurait dû se passer. Cela prend beaucoup de temps et occupe beaucoup de fenêtre contextuelle pour des tâches complexes, et l’analyse doit être vérifiée.
Désormais, Claude connaît immédiatement le flux de travail spécifique et les comportements concernés et peut immédiatement commencer à planifier une enquête (si la documentation est exacte). assez). Cette analyse est structurée avec les informations clés que j’ai besoin de voir. Je n’ai pas besoin d’itérer avec Claude pour produire une analyse dans le format dont j’ai besoin.
Dans ce cas, Claude n’a pas résolu le problème immédiatement, mais la conversation ressemblait davantage à celle que j’aurais pu avoir avec un membre de l’équipe, quelqu’un qui a une compréhension plus approfondie du fonctionnement du système et de ce qui pourrait ne pas fonctionner ici, plutôt que de simplement utiliser Claude pour analyser des modèles dans les données, lire des traces de pile ou résumer des descriptions textuelles du problème.
Précision et hallucinations
Je pense qu’il est juste de se préoccuper de l’exactitude. Nous ne voulons pas faire de choix importants concernant notre système sur la base de détails incomplets ou incorrects. Et il y a eu des inexactitudes importantes que j’ai dû repérer et corriger. (Imaginez si je ne savais pas qu’ils avaient tort.)
J’ai exploré le défi de la précision dans ce post ultérieurmontrant comment nous pouvons utiliser des outils déterministes comme ts-morph pour construire le modèle dont les humains et l’IA peuvent tous deux bénéficier.
Alors voici ce que je pense :
- Parfois, nous n’avons pas besoin d’une précision parfaite. Tant que l’agent choisit la bonne voie, il peut réinspecter certains détails ou approfondir si nécessaire.
- Nous pouvons intégrer des contrôles et des interventions dans nos pipelines CI pour mettre à jour les choses.
- Détruisez et régénérez régulièrement les flux (une fois par trimestre ?).
- Créez des agents de vérification ou des essaims.
Lorsque j’ai repéré une erreur et demandé à un nouvel agent d’analyser le flux à la recherche d’inexactitudes, il a réanalysé le code et a trouvé ce que j’ai vu. Je pense donc que l’option 4 est très crédible : il s’agit simplement d’un effort supplémentaire pour construire un système de vérification (ce qui pourrait rendre l’effort global n’en vaut pas la peine).
Mais je ne suis pas sûr que ce soit la manière optimale d’aborder la situation. Plutôt…
La prochaine phase de l’ingénierie de plate-forme
Il sera essentiel d’éviter d’avoir à procéder à une ingénierie inverse de ces flux. Et je commence à penser que cela deviendra le principal défi des équipes d’ingénierie de plateforme : comment pouvons-nous créer des frameworks et des outils qui exposent notre système sous la forme d’un graphe de dépendances ? Intégré à notre plateforme afin que les agents d’IA n’aient pas besoin de faire de l’ingénierie inverse ; ils peuvent simplement consulter la source de la vérité.
Tout devrait se dérouler de manière transparente pour les ingénieurs logiciels : vous suivez le chemin tracé par la plate-forme et tout fonctionne. Les entreprises qui le font, et en particulier les startups sans héritage, pourraient énormément profiter des agents IA.
Des outils comme Catalogue d’événements sont ici en position de force.
Exemple de flux
Je viens de demander à Claude de traduire l’un des flux de domaine de mon entreprise en un exemple ennuyeux de commerce électronique. La conception et le nom ne sont pas importants ; le type d’information et la visualisation sont ce que j’essaie de transmettre.
N’oubliez pas que ceci est basé sur une journée de piratage. Je suis sûr qu’il existe de nombreuses opportunités d’amélioration ici. Faites-moi savoir si vous avez vu quelque chose de mieux.
Le fichier README
# Place Order with Payment and Fulfillment
**Status**: Active
**Type**: Write Operation
**Complexity**: High
**Last Updated**: 2025-10-19
## Overview
This flow documents the process of placing an order in an ecommerce system, including payment authorization, inventory reservation, and shipment creation. This is the baseline order placement experience where:
- Orders start with `status: 'pending'`
- Payment is authorized before inventory reservation
- Inventory is reserved upon successful payment
- Shipment is created after inventory reservation
## Flow Boundaries
**Start**: Customer clicks "Place Order" button on checkout page
**End**: Publication of `shipping.shipment-created` event (public event with `DOMAIN` scope)
**Scope**: This flow covers the entire process from initial order submission through payment authorization, inventory reservation, shipment creation, and all asynchronous side effects triggered by these operations.
## Quick Reference
### API Endpoints
| Endpoint | Method | Repository | Purpose |
|----------|--------|------------|---------|
| `/checkout` | GET | storefront-app | Checkout page |
| `/api/orders` | POST | order-api | Creates order |
| `/api/payments/authorize` | POST | payment-api | Authorizes payment |
| `/api/inventory/reserve` | POST | inventory-api | Reserves inventory |
| `/api/shipments` | POST | shipping-api | Creates shipment |
| `/api/orders/{orderId}/status` | GET | order-api | Frontend polls for order status |
### Events Reference
| Event Name | Domain | Subject | Purpose | Consumers |
|------------|--------|---------|---------|-----------|
| `private.orders.order.created` | ORDERS | order | Order creation | PaymentHandler, AnalyticsHandler |
| `private.payments.payment.authorized` | PAYMENTS | payment | Payment authorized | InventoryReservationHandler |
| `private.payments.payment.failed` | PAYMENTS | payment | Payment failed | OrderCancellationHandler |
| `private.inventory.stock.reserved` | INVENTORY | stock | Inventory reserved | ShipmentCreationHandler |
| `private.inventory.stock.insufficient` | INVENTORY | stock | Insufficient stock | OrderCancellationHandler |
| `private.shipping.shipment.created` | SHIPPING | shipment | Shipment created | NotificationHandler |
| `shipping.shipment-created` | SHIPPING | shipment | **PUBLIC** Shipment created | External consumers |
### Database Tables
| Table | Operation | Key Fields | Purpose |
|-------|-----------|------------|---------|
| `orders` | INSERT | orderId, customerId, status="pending", totalAmount | Order aggregate storage |
| `order_items` | INSERT | orderItemId, orderId, productId, quantity, price | Order line items |
| `payments` | INSERT | paymentId, orderId, amount, status="authorized" | Payment aggregate storage |
| `inventory_reservations` | INSERT | reservationId, orderId, productId, quantity | Inventory reservation tracking |
| `shipments` | INSERT | shipmentId, orderId, trackingNumber, status="pending" | Shipment aggregate storage |
### Domain Operations
| Aggregate | Method | Purpose |
|-----------|--------|---------|
| Order | `Order.create()` | Creates new order with pending status |
| Order | `Order.confirmPayment()` | Marks payment as confirmed |
| Order | `Order.cancel()` | Cancels order due to payment or inventory failure |
| Payment | `Payment.authorize()` | Authorizes payment for order |
| Payment | `Payment.capture()` | Captures authorized payment |
| Inventory | `Inventory.reserve()` | Reserves stock for order |
| Shipment | `Shipment.create()` | Creates shipment for order |
## Key Characteristics
| Aspect | Value |
|--------|-------|
| Order Status | Uses `status` field: `'pending'` → `'confirmed'` → `'shipped'` |
| Payment Status | Uses `status` field: `'pending'` → `'authorized'` → `'captured'` |
| Inventory Strategy | Reserve-on-payment approach |
| Shipment Status | Uses `status` field: `'pending'` → `'ready'` → `'shipped'` |
## Flow Steps
1. **Customer** navigates to checkout page in storefront-app (`/checkout`)
2. **Customer** reviews order details and clicks "Place Order" button
3. **storefront-app UI** shows loading state with order confirmation message
4. **storefront-app** sends POST request to order-api (`/api/orders`)
- Request includes: customerId, items (productId, quantity, price), shippingAddress, billingAddress
5. **order-api** creates Order aggregate with `status: 'pending'` and persists to database
6. **order-api** creates OrderItem records for each item in the order
7. **order-api** publishes `private.orders.order.created` event
8. **order-api** returns orderId and order details to storefront-app
9. **storefront-app** redirects customer to order confirmation page
### Asynchronous Side Effects - Payment Processing
10. **order-events-consumer** receives `private.orders.order.created` event
11. **PaymentHandler** processes the event
- Calls payment-api to authorize payment
12. **payment-api** calls external payment gateway (Stripe, PayPal, etc.)
13. **payment-api** creates Payment aggregate with `status: 'authorized'` and persists to database
14. **payment-api** publishes `private.payments.payment.authorized` event (on success)
- OR publishes `private.payments.payment.failed` event (on failure)
### Asynchronous Side Effects - Inventory Reservation
15. **payment-events-consumer** receives `private.payments.payment.authorized` event
16. **InventoryReservationHandler** processes the event
- Calls inventory-api to reserve stock
17. **inventory-api** loads Inventory aggregate for each product
18. **inventory-api** calls `Inventory.reserve()` for each order item
- Validates sufficient stock available
- Creates reservation record
- Decrements available stock
19. **inventory-api** creates InventoryReservation records and persists to database
20. **inventory-api** publishes `private.inventory.stock.reserved` event (on success)
- OR publishes `private.inventory.stock.insufficient` event (on failure)
### Asynchronous Side Effects - Shipment Creation
21. **inventory-events-consumer** receives `private.inventory.stock.reserved` event
22. **ShipmentCreationHandler** processes the event
- Calls shipping-api to create shipment
23. **shipping-api** creates Shipment aggregate with `status: 'pending'` and persists to database
24. **shipping-api** calls external shipping carrier API to generate tracking number
25. **shipping-api** updates Shipment with trackingNumber
26. **shipping-api** publishes `private.shipping.shipment.created` event
27. **shipping-events-consumer** receives `private.shipping.shipment.created` event
- **ShipmentCreatedPublicHandler** processes the event
- Loads shipment from repository to get full shipment details
- Publishes public event: `shipping.shipment-created`
- **This marks the END of the flow**
### Order Status Updates
28. Throughout the flow, order-api receives events and updates order status:
- On `private.payments.payment.authorized`: Updates order with paymentId
- On `private.inventory.stock.reserved`: Updates order to `status: 'confirmed'`
- On `private.shipping.shipment.created`: Updates order to `status: 'shipped'`
### Failure Scenarios
**Payment Failure**:
- On `private.payments.payment.failed`: OrderCancellationHandler cancels order
- Order status updated to `'cancelled'`
- Customer notified via email
**Inventory Failure**:
- On `private.inventory.stock.insufficient`: OrderCancellationHandler cancels order
- Payment authorization is voided
- Order status updated to `'cancelled'`
- Customer notified via email with option to backorder
## Repositories Involved
- **storefront-app**: Frontend UI
- **order-api**: Order domain
- **payment-api**: Payment domain
- **inventory-api**: Inventory domain
- **shipping-api**: Shipping and fulfillment domain
- **notification-api**: Customer notifications
## Related Flows
- **Process Refund**: Flow for handling order refunds and returns
- **Update Shipment Status**: Flow for tracking shipment delivery status
- **Inventory Reconciliation**: Flow for syncing inventory counts with warehouse systems
## Events Produced
| Event | Purpose |
|-------|---------|
| `private.orders.order.created` | Notifies that a new order has been created |
| `private.payments.payment.authorized` | Notifies that payment has been authorized |
| `private.payments.payment.failed` | Notifies that payment authorization failed |
| `private.inventory.stock.reserved` | Notifies that inventory has been reserved |
| `private.inventory.stock.insufficient` | Notifies that insufficient inventory is available |
| `private.shipping.shipment.created` | Internal event that shipment has been created |
| `shipping.shipment-created` | **Public event** that shipment is created and ready for carrier pickup |
## Event Consumers
### `private.orders.order.created` Consumers
#### 1. order-events-consumer
**Handler**: `PaymentHandler`
**Purpose**: Initiates payment authorization process
**Actions**:
- Subscribes to event
- Calls `AuthorizePayment` use case
- Invokes payment-api to authorize payment with payment gateway
- Publishes payment result event
#### 2. order-events-consumer
**Handler**: `AnalyticsHandler`
**Purpose**: Tracks order creation for analytics
**Actions**:
- Subscribes to event
- Sends order data to analytics platform
- Updates conversion tracking
### `private.payments.payment.authorized` Consumer
#### payment-events-consumer
**Handler**: `InventoryReservationHandler`
**Purpose**: Reserves inventory after successful payment
**Actions**:
- Subscribes to event
- Calls `ReserveInventory` use case
- Loads order details
- Calls inventory-api to reserve stock for each item
- Publishes inventory reservation result event
### `private.payments.payment.failed` Consumer
#### payment-events-consumer
**Handler**: `OrderCancellationHandler`
**Purpose**: Cancels order when payment fails
**Actions**:
- Subscribes to event
- Calls `CancelOrder` use case
- Updates order status to 'cancelled'
- Triggers customer notification
### `private.inventory.stock.reserved` Consumer
#### inventory-events-consumer
**Handler**: `ShipmentCreationHandler`
**Purpose**: Creates shipment after inventory reservation
**Actions**:
- Subscribes to event
- Calls `CreateShipment` use case
- Calls shipping-api to create shipment record
- Integrates with shipping carrier API for tracking number
- Publishes shipment created event
### `private.inventory.stock.insufficient` Consumer
#### inventory-events-consumer
**Handler**: `OrderCancellationHandler`
**Purpose**: Cancels order when inventory is insufficient
**Actions**:
- Subscribes to event
- Calls `CancelOrder` use case
- Voids payment authorization
- Updates order status to 'cancelled'
- Triggers customer notification with backorder option
### `private.shipping.shipment.created` Consumer
#### shipping-events-consumer
**Handler**: `ShipmentCreatedPublicHandler`
**Purpose**: Converts private shipment event to public event
**Actions**:
- Subscribes to `private.shipping.shipment.created` event
- Loads shipment from repository
- Publishes public event: `shipping.shipment-created`
**Handler**: `NotificationHandler`
**Purpose**: Notifies customer of shipment creation
**Actions**:
- Subscribes to event
- Sends confirmation email with tracking number
- Sends SMS notification (if opted in)
## Database Operations
### orders Table
- **Operation**: INSERT (via upsert)
- **Key Fields**: orderId, customerId, status="pending", totalAmount, createdAt
- **Repository**: `OrderRepository`
### order_items Table
- **Operation**: INSERT (batch)
- **Key Fields**: orderItemId, orderId, productId, quantity, price
- **Repository**: `OrderItemRepository`
### payments Table
- **Operation**: INSERT (via upsert)
- **Key Fields**: paymentId, orderId, amount, status="authorized", gatewayTransactionId
- **Repository**: `PaymentRepository`
### inventory_reservations Table
- **Operation**: INSERT (via upsert)
- **Key Fields**: reservationId, orderId, productId, quantity, reservedAt
- **Repository**: `InventoryReservationRepository`
### shipments Table
- **Operation**: INSERT (via upsert)
- **Key Fields**: shipmentId, orderId, trackingNumber, status="pending", carrier
- **Repository**: `ShipmentRepository`
## External Integrations
- **Payment Gateway Integration**: Authorizes and captures payments via Stripe API
- Endpoint: `/v1/payment_intents`
- Synchronous call during payment authorization
- **Shipping Carrier Integration**: Generates tracking numbers via carrier API
- Endpoint: `/api/v1/shipments`
- Synchronous call during shipment creation
## What Happens After This Flow
This flow ends with the publication of the `shipping.shipment-created` public event, which marks the order as fully processed and ready for carrier pickup.
### State at Flow Completion
- Order: `status: 'shipped'`
- Payment: `status: 'authorized'` (will be captured on actual shipment)
- Inventory: Stock reserved and decremented
- Shipment: `status: 'pending'`, trackingNumber assigned
### Next Steps
After this flow completes:
- Warehouse team picks and packs the order
- Carrier picks up the shipment
- Shipping status updates flow tracks delivery
- Payment is captured upon confirmed shipment
- Customer can track order via tracking number
### External System Integration
Once the `shipping.shipment-created` event is published:
- Warehouse management system begins pick/pack process
- Customer notification system sends tracking updates
- Logistics partners receive shipment manifest
- Analytics systems track fulfillment metrics
## Diagram
See (diagram.mermaid)(./diagram.mermaid) for the complete visual flow showing the progression through systems with horizontal swim lanes for each service.
La Sirène :

flowchart LR
Start((Customer clicks Place Order
on checkout page))
subgraph storefront("🌐 storefront-app")
direction LR
ShowCheckout(Show checkout page)
CustomerReview(Customer reviews order)
ShowConfirmation(Show order
confirmation page)
end
CustomerWaitsForShipment((Customer receives
shipment notification))
subgraph orderService("📦 order-api")
direction LR
CreateOrderEndpoint("POST /api/orders")
OrderCreate(Order.create)
OrderDB((Database:
INSERT orders,
order_items))
PublishOrderCreated("Publish: private.orders
.order.created")
ReceivePaymentAuth("Receive: private.payments
.payment.authorized")
UpdateOrderPayment(Update order
with paymentId)
ReceiveStockReserved("Receive: private.inventory
.stock.reserved")
OrderConfirm(Order.confirmPayment)
UpdateOrderConfirmed((Database:
UPDATE orders
status="confirmed"))
ReceiveShipmentCreated("Receive: private.shipping
.shipment.created")
UpdateOrderShipped((Database:
UPDATE orders
status="shipped"))
end
subgraph paymentService("💳 payment-api")
direction LR
ReceiveOrderCreated("Receive: private.orders
.order.created")
AuthorizeEndpoint("POST /api/payments/
authorize")
PaymentGateway("External: Payment
Gateway API
(Stripe)")
PaymentAuthorize(Payment.authorize)
PaymentDB((Database:
INSERT payments))
PublishPaymentAuth("Publish: private.payments
.payment.authorized")
end
subgraph inventoryService("📊 inventory-api")
direction LR
ReceivePaymentAuth2("Receive: private.payments
.payment.authorized")
ReserveEndpoint("POST /api/inventory/
reserve")
InventoryReserve(Inventory.reserve)
InventoryDB((Database:
INSERT inventory_reservations
UPDATE product stock))
PublishStockReserved("Publish: private.inventory
.stock.reserved")
end
subgraph shippingService("🚚 shipping-api")
direction LR
ReceiveStockReserved2("Receive: private.inventory
.stock.reserved")
CreateShipmentEndpoint("POST /api/shipments")
CarrierAPI("External: Shipping
Carrier API
(FedEx/UPS)")
ShipmentCreate(Shipment.create)
ShipmentDB((Database:
INSERT shipments))
PublishShipmentCreated("Publish: private.shipping
.shipment.created")
ReceiveShipmentCreatedPrivate("Receive: private.shipping
.shipment.created")
LoadShipment(Load shipment
from repository)
PublishPublicEvent("Publish: shipping
.shipment-created")
FlowEnd((Flow End:
Public event published))
end
Start --> ShowCheckout
ShowCheckout --> CustomerReview
CustomerReview --> CreateOrderEndpoint
CreateOrderEndpoint --> OrderCreate
OrderCreate --> OrderDB
OrderDB --> PublishOrderCreated
PublishOrderCreated --> ShowConfirmation
PublishOrderCreated -.-> ReceiveOrderCreated
ReceiveOrderCreated --> AuthorizeEndpoint
AuthorizeEndpoint --> PaymentGateway
PaymentGateway --> PaymentAuthorize
PaymentAuthorize --> PaymentDB
PaymentDB --> PublishPaymentAuth
PublishPaymentAuth -.-> ReceivePaymentAuth
ReceivePaymentAuth --> UpdateOrderPayment
PublishPaymentAuth -.-> ReceivePaymentAuth2
ReceivePaymentAuth2 --> ReserveEndpoint
ReserveEndpoint --> InventoryReserve
InventoryReserve --> InventoryDB
InventoryDB --> PublishStockReserved
PublishStockReserved -.-> ReceiveStockReserved
ReceiveStockReserved --> OrderConfirm
OrderConfirm --> UpdateOrderConfirmed
PublishStockReserved -.-> ReceiveStockReserved2
ReceiveStockReserved2 --> CreateShipmentEndpoint
CreateShipmentEndpoint --> CarrierAPI
CarrierAPI --> ShipmentCreate
ShipmentCreate --> ShipmentDB
ShipmentDB --> PublishShipmentCreated
PublishShipmentCreated -.-> ReceiveShipmentCreated
ReceiveShipmentCreated --> UpdateOrderShipped
PublishShipmentCreated -.-> ReceiveShipmentCreatedPrivate
ReceiveShipmentCreatedPrivate --> LoadShipment
LoadShipment --> PublishPublicEvent
PublishPublicEvent --> FlowEnd
FlowEnd -.-> CustomerWaitsForShipment
style Start fill:#e1f5e1
style FlowEnd fill:#ffe1e1
style CustomerWaitsForShipment fill:#e1f5e1
style PublishOrderCreated fill:#fff4e1
style PublishPaymentAuth fill:#fff4e1
style PublishStockReserved fill:#fff4e1
style PublishShipmentCreated fill:#fff4e1
style PublishPublicEvent fill:#fff4e1
style OrderDB fill:#e1f0ff
style PaymentDB fill:#e1f0ff
style InventoryDB fill:#e1f0ff
style ShipmentDB fill:#e1f0ff
style UpdateOrderConfirmed fill:#e1f0ff
style UpdateOrderShipped fill:#e1f0ff
style PaymentGateway fill:#ffe1f5
style CarrierAPI fill:#ffe1f5

