Zustandsdiagramm Beispiel: Ein Abo-Lebenszyklus als Zustandsautomat

Ein ausgearbeitetes UML-Zustandsdiagramm für ein SaaS-Abo: Zustände, Ereignisse, Guards, Auswahlpunkte, ein zusammengesetzter Zustand und die Bugs, die es früh findet.

· Gründer & Entwickler

Veröffentlicht am

Zustandsdiagramm Beispiel: Ein Abo-Lebenszyklus als Zustandsautomat

Abo-Bugs sind Zustands-Bugs. Ein Kunde behält nach der Kündigung den Zugriff. Ein überfälliges Konto zahlt und bleibt gesperrt. Eine Testphase endet für jemanden ohne Karte, und keine Regel sagt, was dann passiert. Jeder dieser Fälle ist ein Übergang, den niemand gezeichnet hat.

Ein Zustandsautomaten-Diagramm, meist einfach Zustandsdiagramm genannt, listet jeden Zustand auf, in dem ein Objekt sein kann, und jedes Ereignis, das es in einen anderen bringt. Fehlende Übergänge fallen im Review auf statt in Support-Tickets. Dieser Beitrag baut eines für ein SaaS-Abo auf, mit den Status, die Stripe Billing dokumentiert, damit du das Modell mit einem echten System vergleichen kannst, und erklärt die Notation, sobald sie auftaucht.

Wann sich ein Zustandsautomat lohnt

Zeichne einen, wenn ein einzelnes Objekt einen Lebenszyklus mit Regeln hat: eine Bestellung, ein Abo, eine Zahlung, ein Support-Ticket, ein Deployment. Die Warnzeichen kennst du:

  • Eine status-Spalte mit mehr als drei Werten und Code, der sie an vielen Stellen prüft.
  • Boolesche Flags wie is_canceled und is_paused, die sich widersprechen können.
  • Diskussionen darüber, ob etwas aus einem bestimmten Status heraus „passieren kann“.

Für ein Gespräch zwischen mehreren Beteiligten ist ein Zustandsdiagramm das falsche Bild; dort zeigt ein Sequenzdiagramm die Reihenfolge der Nachrichten. Für einen Geschäftsprozess mit vielen Personen und Übergaben passt ein BPMN-Diagramm besser.

Die Notation

Element Notation Bedeutung
Zustand Rechteck mit abgerundeten Ecken Eine Bedingung, in der das Objekt bleibt, bis ein Ereignis eintrifft
Anfangszustand Kleiner gefüllter Kreis Wo der Automat beginnt
Endzustand Gefüllter Kreis in einem Ring Der Automat ist fertig
Übergang Pfeil mit der Beschriftung event [guard] / action Wechsel, wenn das Ereignis eintritt und der Guard wahr ist
Auswahl Raute Guards an den ausgehenden Pfeilen bestimmen das Ziel
Zusammengesetzter Zustand Ein Zustand, der Zustände enthält Regeln, die für alles darin gelten
Internes Verhalten entry /, exit /, do / Läuft beim Eintreten, beim Verlassen oder während des Zustands

Jeder Teil einer Übergangsbeschriftung ist optional. invoice paid ist für sich ein Ereignis, [setting: unpaid] für sich ein Guard, und cancel / revoke access ist ein Ereignis mit Aktion. Syntax und Symbole stammen aus der UML-2.5.1-Spezifikation.

Die Abo-Status

Das Beispiel nutzt die Status aus Stripes Abo-Übersicht, mit automatischen Kartenzahlungen:

Status Was er bedeutet Zugriff
incomplete Die erste Zahlung ist noch nicht erfolgt; der Kunde hat 23 Stunden Nein
incomplete_expired Die erste Zahlung ist nie gelungen; nichts wird berechnet Nein
trialing In einer Testphase Ja
active In gutem Zustand Ja
past_due Die Zahlung der letzten Rechnung ist fehlgeschlagen oder wurde nicht versucht Deine Entscheidung
unpaid Die Wiederholungen sind erschöpft, und deine Einstellungen haben dieses Ergebnis gewählt Nein
paused Die Testphase endete ohne Zahlungsmethode, und Pausieren wurde gewählt Nein
canceled Beendet; Stripe beschreibt es als Endzustand Nein

Viele Produkte lassen den Zugriff während past_due bestehen, solange Zahlungswiederholungen laufen, und dieses Beispiel tut das auch. Schreib diese Entscheidung ins Diagramm; sie ist die, die gern vergessen wird.

Die Übergänge aufbauen

Nimm jeden Zustand und frag, welche Ereignisse dort eintreten können.

  1. Start. subscribe [trial] führt zu trialing; subscribe [no trial] führt zu incomplete.
  2. Erste Zahlung. Aus incomplete führt invoice paid zu active. Vergehen 23 Stunden ohne Zahlung, wechselt das Abo zu incomplete_expired, und Stripe storniert die Rechnung. Dieser Zustand ist endgültig: Für einen neuen Versuch startet der Kunde ein neues Abo.
  3. Ende der Testphase. trial ends führt zu einer Auswahl. [payment succeeds] führt zu active; [no payment method] führt in diesem Beispiel zu paused. Stripe lässt dich wählen, was in diesem Fall passiert: eine Rechnung erstellen (die Voreinstellung), kündigen oder pausieren. Zeichne die Einstellung, die du tatsächlich nutzt, nicht alle drei.
  4. Verlängerungen. Aus active führt renewal payment fails zu past_due, und invoice paid bringt es zurück.
  5. Wiederholungen erschöpft. Noch eine Auswahl: Je nach deinen Einstellungen wechselt das Abo zu unpaid oder canceled oder bleibt past_due. Aus unpaid bringt das Bezahlen der Rechnung es zurück zu active.
  6. Ende. cancel bringt active zu canceled. Kündigung zum Periodenende ist kein eigener Zustand: Das Abo bleibt mit einem Flag active, bis die Periode endet, und wechselt dann zu canceled.

Ein zusammengesetzter Zustand für den Zugriff

Hier gewähren drei Zustände Zugriff: trialing, active und past_due. Zeichnest du sie in einen zusammengesetzten Zustand, Access granted, schreibst du die Regel nur einmal: entry / grant access und exit / revoke access. Jeder Übergang, der den zusammengesetzten Zustand verlässt, entzieht den Zugriff, egal aus welchem Zustand er startet, sodass kein Pfad es vergessen kann. Ein Wechsel zwischen Zuständen innerhalb, etwa von trialing zu active, verlässt ihn nicht und führt diese Aktionen nicht aus.

Ein Startpunkt führt zu trialing oder incomplete. Trialing, active und past due liegen im Rahmen Access granted. Das Testende führt über eine Auswahl zu active oder paused. Eine gescheiterte Verlängerung führt zu past due; erschöpfte Versuche führen zu unpaid oder canceled, dann zum Ende.

15 Komponenten · 17 Verbindungen

In Archboard öffnen
Diagramminhalt
  • Access granted
  • trialing
  • active
  • past_due
  • paused
  • incomplete
  • incomplete_expired
  • unpaid
  • canceled
  • subscribe [trial]
  • subscribe [no trial]
  • invoice paid
  • after 23 h / void invoice
  • trial ends
  • [payment succeeds]
  • [no payment method]
  • resume [payment succeeds]
  • renewal payment fails
  • retries exhausted
  • [setting: unpaid]
  • [setting: cancel]
  • cancel
Ein Zustandsautomat für ein SaaS-Abo mit den Status, die Stripe Billing dokumentiert: die Testphase und die erste Zahlung, ein zusammengesetzter Zustand für alle Status mit Zugriff, fehlgeschlagene Verlängerungen und die zwei Wege, auf denen ein Abo endet.

So liest du es:

  • Oben. Der Startpunkt teilt sich nach der Testphase: trialing im zusammengesetzten Zustand oder incomplete außerhalb. Aus incomplete führt die Zahlung zu active, und 23 Stunden ohne Zahlung führen zu incomplete_expired und zum Endzustand rechts.
  • Mitte, Access granted. trial ends erreicht eine Auswahl: active, wenn die Zahlung gelingt, paused links, wenn es keine Zahlungsmethode gibt. Eine fehlgeschlagene Verlängerung bringt active zu past_due, und eine Zahlung bringt es zurück.
  • Unten. Sind die Wiederholungen erschöpft, schickt eine zweite Auswahl das Abo zu unpaid oder canceled. unpaid kann sich noch zurück zu active bezahlen; canceled endet im Endzustand.
  • Kanten in den Rahmen hinein gewähren Zugriff, Kanten heraus entziehen ihn.

Vom Diagramm zum Code

Das Diagramm bildet auf eine Übergangstabelle ab. Alles, was nicht in der Tabelle steht, wird abgelehnt:

type Status = 'incomplete' | 'incomplete_expired' | 'trialing' | 'active'
  | 'past_due' | 'unpaid' | 'paused' | 'canceled';

const transitions: Record<Status, Partial<Record<string, Status>>> = {
  incomplete: { invoice_paid: 'active', expired: 'incomplete_expired' },
  trialing: { trial_ended_paid: 'active', trial_ended_no_method: 'paused' },
  active: { payment_failed: 'past_due', cancel: 'canceled' },
  past_due: { invoice_paid: 'active', retries_exhausted: 'unpaid' },
  unpaid: { invoice_paid: 'active' },
  paused: { resumed: 'active' },
  incomplete_expired: {},
  canceled: {},
};

function next(status: Status, event: string): Status {
  const target = transitions[status][event];
  if (!target) throw new Error(`${event} is not allowed in ${status}`);
  return target;
}

Kommen die Ereignisse aus den Webhooks eines Zahlungsanbieters, verlass dich nicht auf die Reihenfolge. Stripes Webhook-Dokumentation sagt, dass es nicht garantiert, dass Ereignisse in der Reihenfolge ankommen, in der sie erzeugt wurden. Nimm jedes Ereignis als Anlass, den aktuellen Status des Abos abzurufen, und wende den Übergang darauf an.

Häufige Fehler

  • Fehlende Ereignisse. Prüfe für jeden Zustand jedes Ereignis. „Was passiert, wenn der Kunde während past_due kündigt?“ ist eine Frage, die das Diagramm beantworten sollte.
  • Flags als Zustände gezeichnet. Kündigung zum Periodenende, ein Rabatt oder eine Seat-Anzahl sind Eigenschaften eines Zustands, keine Zustände.
  • Aktionen als Zustände gezeichnet. „Mahn-E-Mail senden“ ist eine Aktion an einem Übergang oder ein entry-Verhalten, kein Ort, an dem das Abo wartet.
  • Kein Endzustand. Zeig, wo der Lebenszyklus endet, damit niemand Code schreibt, der ein gekündigtes Abo wiederbelebt.
  • Guards, die sich überlappen oder Lücken lassen. Die Guards an einer Auswahl müssen jeden Fall genau einmal abdecken; füge bei Bedarf [else] hinzu.
  • Zwei Lebenszyklen in einem Diagramm. Das Abo und seine Rechnungen haben getrennte Zustandsautomaten. Verbinde sie über Ereignisse, statt sie zu verschmelzen.

Halte das Diagramm neben dem Code, der es umsetzt, und prüfe beides im selben Pull Request, wie in Architekturdiagramme, die nicht veralten beschrieben.

In Archboard nachbauen

  1. Melde dich mit Google oder GitHub an und leg ein Diagramm an.
  2. Füge aus dem UML-Pack für jeden Status einen Zustand hinzu.
  3. Füge aus dem Basis-Pack einen Rahmen namens Access granted um trialing, active und past_due hinzu.
  4. Füge aus dem Basis-Pack eine kleine, gefüllte Ellipse für den Anfangszustand hinzu, eine Raute für jede Auswahl und ein Symbol mit einem Kreis mit Punkt für jeden Endzustand.
  5. Verbinde die Zustände mit Kanten, die mit event [guard] / action beschriftet sind.
  6. Füge eine UML-Notiz mit den Ein- und Austrittsaktionen des zusammengesetzten Zustands hinzu.

Das Beispiel nutzt 14 Diagramm-Nodes und bleibt damit im Free-Plan. Mit einem über MCP verbundenen KI-Agenten kannst du dein Status-Enum einfügen und ihn die Zustände entwerfen lassen; jeden Übergang prüfst du dann selbst.

FAQ

Was ist der Unterschied zwischen einem Zustandsdiagramm und einem Flussdiagramm? Ein Flussdiagramm zeigt die Schritte eines Prozesses in ihrer Reihenfolge. Ein Zustandsdiagramm zeigt die Bedingungen, in denen ein Objekt sein kann, und welche Ereignisse es zwischen ihnen bewegen; es beantwortet also, was erlaubt ist, nicht, was als Nächstes passiert.

Sollte jeder Wert meiner Status-Spalte ein Zustand sein? Jeder Wert, der das Verhalten ändert, schon. Verhalten sich zwei Werte in jedem Fall gleich, führe sie zusammen; verbirgt ein Wert zwei Verhaltensweisen, teile ihn.

Wo gehören Webhooks in einen Zustandsautomaten? Sie sind Ereignisse. Prüfe sie, ruf den aktuellen Status beim Anbieter ab und wende den Übergang an, denn Ereignisse können in falscher Reihenfolge oder mehrfach ankommen.

Brauche ich einen zusammengesetzten Zustand? Nutze einen, wenn mehrere Zustände eine Regel teilen, etwa Zugriff oder Abrechnung. Er macht aus einer Regel, die du an vielen Pfeilen wiederholen würdest, eine Eintritts- und eine Austrittsaktion.

Wie detailliert sollten Übergangsbeschriftungen sein? Benenne das Ereignis in fachlichen Begriffen und füge einen Guard nur hinzu, wo zwei Pfeile beim selben Ereignis abgehen. Aktionen, die für die Korrektheit zählen, etwa das Gewähren von Zugriff, gehören ins Diagramm.

Kostenlos starten — ein Seat, keine Kreditkarte.

Ähnliche Beiträge

← Zurück zum Blog

In einer Minute ein Canvas öffnen

Melden Sie sich mit Google oder GitHub an. Kostenlos für einen Sitzplatz. Upgraden Sie, wenn das Team dazukommt.