Créer des flux de travail orientés événements
Coordonnez les services grâce aux événements et à l’idempotence.
Créer des flux de travail orientés événements est une leçon PHP Academy gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage PHP Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours PHP Academy comprend 4 leçons au total.
Flux de travail pilotés par événements
Un message isolé est simple. Un flux de travail — « commande passée → réserver le stock → débiter la carte → expédier → notifier » — qui s’étend sur plusieurs services est précisément le contexte où la conception pilotée par événements révèle sa valeur, et où une approche naïve peut vous jouer des tours.
Cette leçon traite de la chorégraphie et de l’orchestration, du modèle de boîte d’envoi pour une publication atomique, des sagas pour l’annulation distribuée et de l’idempotence qui fait tenir l’ensemble.
Chorégraphie ou orchestration
Deux façons de coordonner des flux en plusieurs étapes :
- Chorégraphie — chaque service réagit aux événements et émet les siens ; il n’y a pas de cerveau central. Le couplage est faible, mais le flux global reste implicite et difficile à suivre.
- Orchestration — un coordinateur central indique à chaque service quoi faire ensuite. Le fonctionnement est explicite et observable, mais l’orchestrateur devient un point de couplage.
Règle générale : choisissez la chorégraphie pour une diffusion multiple simple, et l’orchestration lorsqu’un flux comporte de nombreuses étapes ordonnées et nécessite un état visible.
Événements ou commandes
Nommez vos messages avec précision :
- Un événement énonce un fait passé :
OrderPlaced. L’émetteur ne se préoccupe pas de savoir qui l’écoute. - Une commande demande une action future à un gestionnaire précis :
ChargeCard.
Les événements pilotent la chorégraphie ; les commandes pilotent l’orchestration. Mélanger les deux vocabulaires (un « événement » qui attend en réalité un seul gestionnaire) est une source courante de couplage invisible.
<?php
final class OrderPlaced {
public function __construct(
public readonly string $orderId,
public readonly string $customerId,
public readonly int $amountCents,
public readonly string $occurredAt,
) {}
}
$e = new OrderPlaced('o-42', 'c-7', 1990, gmdate('c'));
echo json_encode($e), "\n";Problème de double écriture
Le bogue classique : un gestionnaire met à jour la base de données et publie un message au moyen de deux opérations distinctes. Si le processus s’arrête entre les deux, vous obtenez une incohérence — la ligne a été modifiée, mais aucun événement n’a été envoyé, ou l’inverse.
<?php
// BROKEN: not atomic. A crash between the two lines corrupts state.
function placeOrder(PDO $db, $broker, array $o): void {
$db->prepare('INSERT INTO orders ...')->execute($o);
// <-- crash here = row exists but no event ever published
$broker->publish('OrderPlaced', json_encode($o));
}Boîte d’envoi transactionnelle
La solution est le modèle de boîte d’envoi : dans la même transaction de base de données que celle qui modifie vos données, insérez l’événement dans une table outbox. Un processus de relais distinct lit les lignes non publiées et les envoie au courtier. Une seule validation atomique, sans double écriture.
<?php
function placeOrder(PDO $db, array $o): void {
$db->beginTransaction();
$db->prepare('INSERT INTO orders (id, total) VALUES (?, ?)')
->execute([$o['id'], $o['total']]);
// Same transaction -> atomic with the business write
$db->prepare('INSERT INTO outbox (id, type, payload) VALUES (?, ?, ?)')
->execute([bin2hex(random_bytes(8)), 'OrderPlaced', json_encode($o)]);
$db->commit();
}Le relais (producteur)
Un processus interroge régulièrement la boîte d’envoi (ou suit le journal des modifications de la base de données via CDC), publie chaque ligne, puis la marque comme envoyée. Comme le relais peut s’arrêter après la publication, mais avant le marquage, il fonctionne lui aussi selon une sémantique « au moins une fois » — ce qui convient, puisque les consommateurs sont idempotents.
<?php
function relayOutbox(PDO $db, $broker): void {
$rows = $db->query(
'SELECT id, type, payload FROM outbox
WHERE published_at IS NULL ORDER BY created_at LIMIT 100'
)->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $r) {
$broker->publish($r['type'], $r['payload'], messageId: $r['id']);
$db->prepare('UPDATE outbox SET published_at = now() WHERE id = ?')
->execute([$r['id']]);
}
}Consommateurs idempotents (encore)
Comme le relais et le courtier fonctionnent tous deux selon une sémantique « au moins une fois », les gestionnaires en aval verront des doublons. Chaque consommateur enregistre l’identifiant du message qu’il a traité et ignore immédiatement les répétitions — le même mécanisme de déduplication que vous avez appris précédemment, maintenant appliqué à chaque service.
<?php
function onOrderPlaced(PDO $db, string $messageId, array $data): void {
$db->beginTransaction();
try {
$db->prepare('INSERT INTO inbox (message_id) VALUES (?)')
->execute([$messageId]); // unique index = dedup
} catch (PDOException $e) {
$db->rollBack();
return; // already handled this message
}
reserveStock($data['orderId']);
$db->commit();
}
function reserveStock(string $id): void {}Sagas : annulation distribuée
Vous ne pouvez pas ouvrir une seule transaction ACID entre plusieurs services. Une saga modélise un flux de longue durée comme une suite de transactions locales, chacune accompagnée d’une action compensatoire qui l’annule. Si l’étape 3 échoue, vous exécutez les compensations des étapes 2 et 1 dans l’ordre inverse.
Exemple : le paiement échoue après la réservation du stock → émettez ReleaseStock pour compenser. Il n’existe aucune annulation automatique — vous devez concevoir l’action inverse pour chaque étape.
Une saga orchestrée
Un orchestrateur pilote la saga : il avance en cas de succès et déclenche les compensations en cas d’échec. Conservez l’état de la saga afin qu’un plantage puisse entraîner sa reprise.
<?php
function handleStepResult(array $saga, string $step, bool $ok, $bus): array {
if ($ok) {
$next = ['reserveStock' => 'chargeCard', 'chargeCard' => 'ship'][$step] ?? null;
if ($next) { $bus->send($next, $saga['orderId']); $saga['state'] = $next; }
else { $saga['state'] = 'completed'; }
} else {
// Run compensations in reverse for whatever already succeeded
foreach (array_reverse($saga['done']) as $s) {
$bus->send('compensate.' . $s, $saga['orderId']);
}
$saga['state'] = 'compensating';
}
return $saga;
}Délais d’expiration dans les flux longs
Une étape de saga peut tout simplement ne jamais répondre — le service de paiement est indisponible ou une approbation humaine n’arrive jamais. Sans délai d’expiration, la saga reste bloquée indéfiniment tout en conservant les réservations. Enregistrez une échéance pour chaque étape ; un planificateur recherche les sagas arrivées à échéance et déclenche le parcours d’échec et de compensation.
<?php
function reapTimedOutSagas(PDO $db, $bus): void {
$rows = $db->query(
"SELECT order_id, state FROM sagas
WHERE state NOT IN ('completed','compensating')
AND deadline_at < now()"
)->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $r) {
echo "Saga {$r['order_id']} timed out at step {$r['state']}\n";
$bus->send('saga.compensate', $r['order_id']); // trigger rollback
}
}Versionnement et observabilité
Les flux de travail s'étendent sur plusieurs années ; les événements doivent évoluer en toute sécurité :
- Ajoutez une
version(ou un schéma) à chaque événement ; les consommateurs tolèrent les nouveaux champs inconnus et ne supposent jamais qu'un champ est présent. - Privilégiez les modifications additives ; ne détournez jamais le sens d'un champ existant.
- Propagez un identifiant de corrélation dans chaque message afin de pouvoir suivre une transaction métier à travers tous les services dans vos journaux et votre traçage.
Sans identifiants de corrélation, déboguer un flux orchestré à travers cinq services est presque impossible.
<?php
$envelope = [
'type' => 'OrderPlaced',
'version' => 2,
'correlationId' => $incoming['correlationId'] ?? bin2hex(random_bytes(8)),
'occurredAt' => gmdate('c'),
'data' => ['orderId' => 'o-42'],
];
echo json_encode($envelope, JSON_PRETTY_PRINT), "\n";Vérification rapide
Éviter le problème de double écriture.
Bilan
Vous pouvez maintenant concevoir des flux de travail fiables pilotés par les événements :
- Choisissez la chorégraphie (événements) ou l'orchestration (commandes) selon la complexité du flux.
- Résolvez le problème de double écriture avec la boîte de sortie transactionnelle et un relais.
- Rendez chaque consommateur idempotent grâce à une clé de boîte de réception/de déduplication.
- Utilisez des sagas avec des actions compensatoires pour effectuer des restaurations distribuées.
- Faites évoluer les événements de manière additive et transmettez un identifiant de corrélation pour assurer la traçabilité.
Ces modèles transforment des messages disparates en processus métier fiables et observables.
Questions Fréquemment Posées
La leçon « Créer des flux de travail orientés événements » est-elle gratuite ?
Oui — le texte complet de « Créer des flux de travail orientés événements » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours PHP Academy, passe à CoddyKit PRO. Le cours PHP Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Créer des flux de travail orientés événements » ?
Coordonnez les services grâce aux événements et à l’idempotence. Tu pratiques PHP Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer PHP Academy ?
Aucune expérience préalable n'est requise. PHP Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.
Combien de temps prend la leçon « Créer des flux de travail orientés événements » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon PHP Academy ?
Oui. Chaque leçon PHP Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Pourquoi la messagerie asynchrone
- Travailler avec RabbitMQ en PHP
- Apache Kafka avec PHP
- Créer des flux de travail orientés événements