Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions documentation/user/use-cases/_index.fr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
title: Cas d'usage
weight: 45
---

# Cas d'usage

Le reste de ce guide est de la référence : une page par fonctionnalité, `await` ici, les signaux
là, Nexus plus loin. Cette section fait l'inverse. Chaque entrée est **une chose entière** —
plusieurs applications, plusieurs mécanismes, un problème qui existe en dehors de Durable — avec
son code dans le dépôt et de quoi la lancer.

Ce ne sont pas des exercices avancés. Aucune n'est difficile ; elles sont *complètes*. C'est la
seule chose qui les distingue d'un exemple de la section [Écrire un workflow](../workflows/).

| | |
|---|---|
| [Quatre applications qui s'appellent](nexus-demo/) | trois frameworks, quatre namespaces Temporal, un contrat partagé — et une exécution qui sert une opération pendant qu'elle en appelle une autre |

Une seconde entrée — un agent IA interruptible, dont la boucle est pilotée depuis du code de
workflow — attend que son prototype se pose. Elle arrivera avec de quoi la lancer : une entrée qui
renvoie vers quelque chose qu'on ne peut pas démarrer n'est pas une entrée.

## Ce qu'une entrée doit contenir

Pour que la section reste lisible quand elle grandira, chaque page dit, dans cet ordre :

1. **le problème**, formulé sans le mot « Durable » ;
2. **ce qui est construit** — les fichiers, où ils sont ;
3. **ce que Durable apporte**, et surtout **ce qu'il n'apporte pas** ;
4. **comment on la lance** ;
5. **ce qui n'est pas prouvé.** Une entrée sans cette partie est une brochure.
32 changes: 32 additions & 0 deletions documentation/user/use-cases/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
title: Use cases
weight: 45
---

# Use cases

The rest of this guide is reference material: one page per feature — `await` here, signals there,
Nexus further on. This section does the opposite. Each entry is **a whole thing** — several
applications, several mechanisms, a problem that exists outside Durable — with its code in the
repository and a way to run it.

These are not advanced exercises. None of them is hard; they are *complete*. That is the only
thing separating them from an example in [Writing a workflow](../workflows/).

| | |
|---|---|
| [Four applications calling each other](nexus-demo/) | three frameworks, four Temporal namespaces, one shared contract — and an execution that serves one operation while calling another |

A second entry — an interruptible AI agent, its loop driven from workflow code — is waiting for its
prototype to settle. It will land with a way to run it: an entry pointing at something you cannot
start is not an entry.

## What an entry must contain

So the section stays readable as it grows, every page says, in this order:

1. **the problem**, stated without the word "Durable";
2. **what was built** — the files, and where they live;
3. **what Durable brings**, and above all **what it does not**;
4. **how to run it**;
5. **what is not proven.** An entry without that part is a brochure.
105 changes: 105 additions & 0 deletions documentation/user/use-cases/nexus-demo.fr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
---
title: Quatre applications qui s'appellent
weight: 10
---

# Quatre applications qui s'appellent

## Le problème

Une commande traverse quatre systèmes qui n'appartiennent pas à la même équipe : la boutique retient
le stock, le métier facture, la logistique planifie et expédie, l'ERP suit. Chacun a son dépôt, son
framework, son rythme de déploiement. Aucun n'a envie d'importer le code d'un autre.

La façon habituelle de coudre ça — une API HTTP par service, un client par appelant, un retry par
client, un timeout par retry — marche jusqu'au jour où l'un des quatre est éteint pendant la
transaction. Alors quelqu'un doit décider si on attend, si on rejoue, et ce qu'il advient de ce qui
a déjà été pris.

## Ce qui est construit

Quatre applications, quatre namespaces Temporal, trois frameworks. Elles vivent dans le dépôt, sous
[`sylius/`](https://github.com/gplanchat/durable-dev/tree/main/sylius),
[`symfony/`](https://github.com/gplanchat/durable-dev/tree/main/symfony),
[`magento/`](https://github.com/gplanchat/durable-dev/tree/main/magento) et
[`laravel/`](https://github.com/gplanchat/durable-dev/tree/main/laravel).

| | la boutique | le métier | le banc Magento | la logistique |
|---|---|---|---|---|
| framework | Sylius | Symfony | Mage-OS | Laravel |
| sert | `stock` | `facturation` | — | `livraison` |
| appelle | `facturation` | `stock` | les trois | `stock`, **depuis le workflow qui sert** |
| PHP | 8.3 | 8.3 | 8.2 | 8.2 |

Les quatre lisent le même paquet de contrats, `src/DurableDemoContracts/`. **Rien d'autre ne circule
entre elles** : pas de client HTTP, pas de SDK partagé, pas de classe d'implémentation.

## Ce que Durable apporte

**Appeler ne demande rien.** `WorkflowEnvironment::nexusStub()` lit le contrat par réflexion.
Servir se câble une fois par hôte — et se câble *hors* de Symfony : la logistique enregistre ses
gestionnaires avec deux classes et six lignes de `config/durable.php`, le banc Magento câble en
`di.xml`. La moitié servante de Nexus n'est pas une fonctionnalité du bundle.

**Les deux formes s'écrivent pareil.** `CommandeWorkflow` appelle `verifier` puis `encaisser` sur le
même stub. La première revient en quelques millisecondes, servie par une méthode ordinaire ; la
seconde prend une quinzaine de secondes, remplie par un workflow d'en face. **Le code de l'appelant
ne distingue pas les deux**, et c'est tout le sujet.

**L'attente ne tient rien d'ouvert.** Pendant une mise au point, le worker qui devait faire avancer
l'encaissement est resté éteint quatre minutes. L'opération est restée en
`NEXUS_OPERATION_STARTED`, l'appelant n'a rien consommé, et tout s'est terminé normalement quand le
worker est revenu. Aucune connexion, aucun processus, aucune transaction n'attendait. Refait depuis
Magento : 49 secondes, même résultat.

## Ce qu'il n'apporte pas

**Pas la compensation.** Aucun des trois contrats n'a d'opération qui rende ce qu'il a pris. La
seule protection est **l'ordre des appels** : `CommandeNexusWorkflow` demande d'abord tout ce qui
peut dire non — vérifier la facture, planifier la tournée, retenir le stock — et n'engage
qu'ensuite. Les deux ordres inverses ont été écrits d'abord et mesurés : une commande en USD
retenait le stock avant de se faire refuser la facture, et une commande de six colis était
**encaissée** avant que la logistique ne refuse de la porter.

**Pas l'idempotence.** Une tâche Nexus est redélivrée ; c'est le gestionnaire qui doit tenir. Celui
de `stock` écrit son verdict dans `app_durable_stock_reservation`, clé par identifiant de commande —
rejouer la même commande rend le même verdict et ne retient pas de stock une seconde fois. Ça a
été écrit à la main, Durable ne l'a pas fourni.

## Comment on la lance

```bash
bin/demo-nexus # d'abord : les namespaces et les endpoints Nexus
demo/lancer.sh # ensuite : les huit workers
demo/lancer.sh --etat # dit qui tourne
demo/lancer.sh --arreter # les arrête
```

`bin/demo-nexus` passe en premier, et il n'est pas facultatif : les workers se connectent à des
endpoints qui n'existent pas tant qu'il ne les a pas créés. Les deux scripts impriment, une fois
finis, les commandes d'appel avec les bonnes valeurs.

L'ordre de démarrage n'a pas d'importance : un worker en retard fait attendre, il ne fait pas
échouer.

Deux prérequis qui ne se devinent pas, et que
[`demo/README.md`](https://github.com/gplanchat/durable-dev/blob/main/demo/README.md) détaille :

- **un serveur Temporal dont les API Nexus sont actives.** `temporal server start-dev` convient ;
`temporalio/auto-setup:1.25.2` répond `Nexus APIs are disabled` à la création d'endpoint ;
- **deux binaires PHP.** 8.3 pour les deux maquettes Symfony, 8.2 pour Magento et Laravel — c'est
mesuré, pas frileux : sur le poste de référence aucune version unique n'a l'intersection des
extensions exigées.

## Ce qui n'est pas prouvé

- **La montée en charge.** Quatre applications sur un poste, un serveur `start-dev`, une commande à
la fois. Rien ici ne dit ce que fait une file Nexus sous charge réelle.
- **La reprise après un échec du gestionnaire servant.** Ce qui a été mesuré, c'est un worker
*éteint* — pas un gestionnaire qui lève au milieu de son travail.
- **La sécurité.** Les quatre namespaces sont sur le même serveur sans mTLS ni autorisation. Le
cloisonnement inter-équipes, qui est la moitié de l'argument Nexus, n'est pas démontré.

Le détail de ce que chaque maquette a ajouté, maquette par maquette, est dans
[`demo/README.md`](https://github.com/gplanchat/durable-dev/blob/main/demo/README.md). La mécanique
Nexus elle-même est décrite dans [Opérations Nexus](../../nexus/).
101 changes: 101 additions & 0 deletions documentation/user/use-cases/nexus-demo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
---
title: Four applications calling each other
weight: 10
---

# Four applications calling each other

## The problem

An order crosses four systems owned by four different teams: the shop holds stock, the business
side invoices, logistics plans and ships, the ERP follows. Each has its own repository, framework
and release cadence. None of them wants to import another one's code.

The usual way to stitch that together — one HTTP API per service, one client per caller, one retry
per client, one timeout per retry — works until one of the four is down mid-transaction. Then
someone has to decide whether to wait, whether to replay, and what happens to what was already
taken.

## What was built

Four applications, four Temporal namespaces, three frameworks. They live in the repository under
[`sylius/`](https://github.com/gplanchat/durable-dev/tree/main/sylius),
[`symfony/`](https://github.com/gplanchat/durable-dev/tree/main/symfony),
[`magento/`](https://github.com/gplanchat/durable-dev/tree/main/magento) and
[`laravel/`](https://github.com/gplanchat/durable-dev/tree/main/laravel).

| | the shop | the business side | the Magento bench | logistics |
|---|---|---|---|---|
| framework | Sylius | Symfony | Mage-OS | Laravel |
| serves | `stock` | `facturation` | — | `livraison` |
| calls | `facturation` | `stock` | all three | `stock`, **from the workflow that serves** |
| PHP | 8.3 | 8.3 | 8.2 | 8.2 |

All four read the same contract package, `src/DurableDemoContracts/`. **Nothing else travels between
them**: no HTTP client, no shared SDK, no implementation class.

## What Durable brings

**Calling requires nothing.** `WorkflowEnvironment::nexusStub()` reads the contract by reflection.
Serving is wired once per host — and it wires up *outside* Symfony: logistics registers its handlers
with two classes and six lines of `config/durable.php`, the Magento bench wires in `di.xml`. The
serving half of Nexus is not a bundle feature.

**Both shapes are written the same way.** `CommandeWorkflow` calls `verifier`, then `encaisser`, on
the same stub. The first returns in milliseconds, served by an ordinary method; the second takes
about fifteen seconds, fulfilled by a workflow on the other side. **The caller's code does not tell
them apart**, and that is the whole point.

**Waiting holds nothing open.** During a debugging session the worker that was to advance the
payment stayed down for four minutes. The operation stayed in `NEXUS_OPERATION_STARTED`, the caller
consumed nothing, and everything completed normally when the worker came back. No connection, no
process, no transaction was waiting. Repeated from Magento: 49 seconds, same result.

## What it does not bring

**Not compensation.** None of the three contracts has an operation that gives back what it took. The
only protection is **call ordering**: `CommandeNexusWorkflow` first asks everything that can say no
— check the invoice, plan the round, hold the stock — and only then commits. Both reverse orders
were written first, and measured: a USD order held stock before being refused an invoice, and a
six-parcel order was **charged** before logistics refused to carry it.

**Not idempotency.** A Nexus task gets redelivered; the handler has to hold. The `stock` handler
writes its verdict to `app_durable_stock_reservation`, keyed by order id — replaying the same order
returns the same verdict and does not hold stock twice. That was written by hand; Durable did not
provide it.

## How to run it

```bash
bin/demo-nexus # first: the namespaces and the Nexus endpoints
demo/lancer.sh # then: the eight workers
demo/lancer.sh --etat # report who is running
demo/lancer.sh --arreter # stop them
```

`bin/demo-nexus` comes first and is not optional: the workers connect to endpoints that do not
exist until it has created them. Both scripts print the call commands with the right values once
they are done.

Start order does not matter: a late worker makes things wait, it does not make them fail.

Two prerequisites you would not guess, detailed in
[`demo/README.md`](https://github.com/gplanchat/durable-dev/blob/main/demo/README.md):

- **a Temporal server with the Nexus APIs enabled.** `temporal server start-dev` will do;
`temporalio/auto-setup:1.25.2` answers `Nexus APIs are disabled` on endpoint creation;
- **two PHP binaries.** 8.3 for the two Symfony apps, 8.2 for Magento and Laravel — measured, not
timid: on the reference machine no single version has the intersection of required extensions.

## What is not proven

- **Scale.** Four applications on one machine, a `start-dev` server, one order at a time. Nothing
here says what a Nexus queue does under real load.
- **Recovery from a failing serving handler.** What was measured is a worker that was *down* — not a
handler that throws halfway through its work.
- **Security.** The four namespaces sit on the same server with no mTLS and no authorization.
Cross-team isolation, which is half the Nexus argument, is not demonstrated.

What each app added, one by one, is in
[`demo/README.md`](https://github.com/gplanchat/durable-dev/blob/main/demo/README.md). The Nexus
mechanics themselves are described in [Nexus operations](../../nexus/).
Loading