Idempotency Key (côté émetteur)
Le pendant émetteur du Idempotent Receiver. Sans clé d'idempotence stable côté émetteur, la déduplication côté récepteur ne sert à rien.
Problème
Un retry naïf qui régénère un nouveau UNB CTRL, un nouveau
ISA13 ou un nouveau eb:MessageId à chaque
tentative casse complètement le pattern Idempotent Receiver :
le partenaire reçoit deux clés différentes, conclut qu'il s'agit de deux
commandes distinctes et intègre deux fois. Le pattern d'idempotence ne
suffit pas côté récepteur — il doit être amorcé côté émetteur.
Forces
- Persister la clé est non-négociable. Un compteur en mémoire est perdu au redémarrage : la clé doit vivre dans une base, dans une outbox ou dans le journal d'envoi.
- La clé doit être générée avant le premier envoi, pas à l'envoi. Sinon un crash après l'envoi mais avant la persistance laisse le système incapable de retenter avec la même clé.
- Une clé unique par intention métier, pas par tentative réseau. Une commande métier = une clé, peu importe le nombre de retries.
- La clé doit être contractuelle. L'émetteur et le récepteur doivent s'accorder sur sa structure, sa longueur et sa fenêtre d'unicité (souvent 90 jours en EDI).
Solution
Au moment où l'application métier décide d'émettre un message, elle
génère une clé d'idempotence déterministe (typiquement <type>-<date>-<compteur> ou un UUID v4
persisté), la stocke avec le message à émettre dans une outbox, puis
laisse le sender worker l'utiliser comme UNB CTRL / ISA13 / eb:MessageId à chaque tentative.
Le code naïf devient un workflow à deux étapes : enregistrer
l'intention (avec sa clé), puis exécuter l'envoi (qui peut être
retenté autant de fois qu'il le faut avec la même clé).
T0 Application métier → outbox.insert(domainEvent, idempotencyKey=ORD-20260514-0001)
T1 Sender worker → SELECT ... FROM outbox WHERE status='pending'
T2 Sender worker → POST AS2 avec UNB CTRL = ORD-20260514-0001
T3 réseau ↛ erreur réseau, pas de MDN reçu
T4 Sender worker → retry (mêmes octets) en réutilisant la même clé
T5 partenaire reconnaît un doublon (Idempotent Receiver) → réémet le même MDN
T6 Sender worker → outbox.markDelivered(idempotencyKey) Implémentation EDI
En EDI, le sender worker lit l'outbox, prend le tuple (idempotencyKey, payload), écrit la clé dans le segment
d'enveloppe correspondant (UNB+...+ORD-20260514-0001' en
EDIFACT,
ISA13=900042001 en X12,
eb:MessageId en AS4) et l'envoie. Une retransmission
rejoue la même ligne d'outbox avec la même clé. La table d'outbox
porte un statut (pending, sent, acked) et une colonne de tentatives, pour observer les
redondances réseau. Walmart, Stellantis, OpenPEPPOL imposent tous une
clé d'idempotence stable côté émetteur dans leurs manuels partenaires.
Anti-patterns
- Nouvelle clé à chaque retry. Annihile la déduplication côté récepteur — c'est exactement ce que le pattern doit empêcher.
- Compteur en mémoire. Crash = compteur réinitialisé = collisions ou duplications.
- Clé générée à l'envoi, pas avant. Si le worker crashe entre l'envoi et la persistance, on perd la traçabilité du retry.
- Clé déconnectée de l'intention métier. Si la même commande peut produire deux clés, on se prive du bénéfice de l'idempotence.
Patterns liés
- Idempotence (récepteur) — le pattern complémentaire qu'il faut amorcer.
- Outbox — le support naturel de la persistance de la clé.
- Retry & backoff — la politique qui rejoue avec la même clé.
Sources
- Hohpe G., Woolf B. — Enterprise Integration Patterns, pattern Idempotent Receiver. enterpriseintegrationpatterns.com — Idempotent Receiver
- Stripe API — Idempotency Keys. La référence contemporaine la plus déployée du pattern, au-delà de l'EDI. stripe.com/docs/api/idempotent_requests
- IETF draft — HTTP Idempotency-Key Header. La tentative de standardisation IETF du header transverse à HTTP. datatracker.ietf.org — Idempotency-Key
- Walmart Supplier Center — EDI Guidelines. Convention
sur la structure des
UNB CTRLetISA13côté émetteur.