Ingénierie inverse de votre architecture logicielle avec Claude Code pour aider Claude Code – O’Reilly


Exemple de flux d’architecture rétro-ingéniéré par Claude Code

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 :

Fichiers de conception du système

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 :

  1. 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.
  2. Nous pouvons intégrer des contrôles et des interventions dans nos pipelines CI pour mettre à jour les choses.
  3. Détruisez et régénérez régulièrement les flux (une fois par trimestre ?).
  4. 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 :

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



Source link

Translate »
Attajir
Logo
Shopping cart