Schreiben von langlebigem Code (Durable Code)
Ein Workflow ist eine einfache PHP-Klasse. Keine Basisklasse, kein Interface, kein Framework-Typ in ihrer Signatur. Was ihn langlebig macht, ist, dass die Engine ihn aus einem Journal wiederholt – daher ist die einzige wirklich bindende Regel die Determinismus.
Die drei Formen
#[AsWorkflow('checkout')] // der Name, den der Cluster kennt
final class CheckoutWorkflow
{
public function __construct(
private readonly WorkflowEnvironment $environment,
) {}
#[AsWorkflowMethod] // genau eine pro Workflow-Klasse
public function run(string $orderId): string
{
$orders = $this->environment->activityStub(OrderActivities::class);
$receipt = $this->environment->await($orders->charge($orderId));
$this->environment->sleep(Duration::seconds(30), 'Abkühlphase');
return $this->environment->await($orders->notify($receipt));
}
}
interface OrderActivities // der VERTRAG trägt die Namen
{
#[AsActivityMethod(name: 'shop.order.charge')]
public function charge(string $orderId): string;
}
final class DoctrineOrderActivities implements OrderActivities // der HANDLER erledigt die Arbeit
{
public function charge(string $orderId): string { /* echte Seiteneffekte hier */ }
}
#[AsNexusService('billing')] // ein anderes Team, eine andere Bereitstellung
interface BillingContract
{
#[AsNexusOperation('check')]
public function check(string $order, int $amountInCents, string $currency): array;
}
Schreiben Sie einen Aktivitätsnamen niemals als String an der Aufrufstelle. Der Stub liest die Attribute des Vertrags, daher lebt der Name an genau einer Stelle. Dies ist der mit Abstand häufigste Weg, einen bestehenden Workflow zu brechen: Benennen Sie die Methode um, behalten Sie das Attribut bei, und laufende Ausführungen finden ihre Aktivität trotzdem.
Die bindende Regel: Der Workflow-Körper wird wiederholt
Alles in einer #[AsWorkflowMethod]-Methode wird jedes Mal von vorne ausgeführt, wenn die Engine die Ausführung fortsetzt – möglicherweise in einem anderen Prozess, Tage später. Daher muss der Körper beim Wiederholen die gleichen Antworten liefern:
| Niemals im Workflow-Körper | Verwenden Sie stattdessen |
|---|---|
rand(), uniqid(), time(), new \DateTime() | $environment->sideEffect(fn() => …) – das Ergebnis wird einmal protokolliert |
sleep(), usleep() | $environment->sleep($duration, 'warum wir warten') |
| Eine Datenbankabfrage, ein HTTP-Aufruf, ein Dateischreibvorgang | eine Aktivität |
if (getenv(…)) zur Verzweigung bei Bereitstellung | $environment->version($changeId, $min, $max) |
Eine Aktivität ist der Ort, an den Nichtdeterminismus gehört. Sie wird einmal pro Versuch ausgeführt, ihr Ergebnis wird im Journal festgehalten, und bei Wiederholung antwortet das Journal anstelle des Codes.
⚠ Eine Aktivität wird wiederholt; schreiben Sie sie so, dass ein zweiter Versuch sicher ist. Die Engine garantiert, dass das Ergebnis einmal aufgezeichnet wird, nicht, dass Ihr Seiteneffekt nur einmal auftritt. Eine Karte zweimal zu belasten ist ein geschäftlicher Vorfall, kein Framework-Fehler.
Warten
Alles Planbare gibt ein Awaitable zurück. Nichts passiert, bis Sie darauf warten – das ist auch der Weg zur Parallelität:
$a = $stub->reserve($order); // geplant, nicht abgewartet
$b = $stub->quote($order);
[$reserved, $quoted] = $this->environment->await($this->environment->all($a, $b));
$first = $this->environment->await($this->environment->any($a, $b));
$twoOfThree = $this->environment->await($this->environment->some(2, $a, $b, $c));
await() akzeptiert eine optionale Frist: await($awaitable, Duration::minutes(5)).
Referenzdateien
Lesen Sie die benötigte Datei, nicht alle drei:
references/workflows.md– Signale, Updates, Child-Workflows, Timer,continueAsNew, Versionierung und wie eine Ausführung gestartet wird (niemals inline in einer Webanfrage).references/activities.md–ActivityOptions, Wiederholungsstrategie, die vier Timeouts, nicht wiederholbare Ausnahmen, Heartbeats, Abbruch.references/nexus.md– Nexus-Verträge, warum sich ein Vertrag in zwei aufspaltet, wenn ein Workflow eine Operation erfüllt, und die Nutzlastfalle, die stillschweigend fehlschlägt.references/migrations.md– Lesen Sie dies, bevor Sie vorhandenen Code berühren. Wechsel vom Temporal PHP SDK oder von einer Durable-Version zur nächsten:gplanchat/durable-rectorübernimmt das Umschreiben, und die Referenz handelt hauptsächlich von den fünf Dingen, die seine Regeln nicht erraten können – zwei davon schlagen stillschweigend auf einem bereits laufenden Server fehl.
Bevor Sie den Code zurückgeben
- Eine
#[AsWorkflowMethod]-Methode pro Workflow-Klasse, und die Klasse ist dem Host deklariert (Symfony/Sylius autoconfiguren sie; Magento listet sie indi.xml). - Jeder Aktivitätsaufruf erfolgt über einen aus einem Vertrag erstellten Stub – keine String-Namen.
- Nichts Nichtdeterministisches im Workflow-Körper.
- Jeder Timer hat eine Zusammenfassung: Sie benennt die Zeile im Beobachtungszeitstrahl, und
TimerScheduledsagt einem Operator um 3 Uhr morgens gar nichts. - Der Workflow fängt nichts ab, was er nicht kompensieren kann. Ein nicht behandelter Fehler beendet die Ausführung und ist sichtbar; ein verschluckter hinterlässt eine halb verarbeitete Bestellung und schweigt.