Skip to content

Modul 12: Typestate & Event-Sourcing

Typisierbare Zustandsmaschinen fuer eine ganze Domain. Wie Typestate Validez erzwingt und wie Event Sourcing die Historie bewahrt. Paper: Typestate for Rust arXiv 2609.04372 https://arxiv.org/abs/2609.04372

Fortschritt0/5 Lektionen
58

Typestate & Event-Sourcing

Lektion 58 von 5

Warum Zustandsmaschinen typsicher sein wollen

Domain-Driven Design sagt: Unterteile dein System in Aggregate mit klaren Konsistenzregeln. Eine Bestellung ist neu, wird bezahlt, dann versendet — nie rueckwaerts, nie willkuerlich. In klassischem OO prueft man solche Regeln zur Laufzeit: if (!order.canShip()) throw. Typestate verschiebt solche Pruefungen ins Typsystem: Der Typ selbst verbietet ungueltige Operationen zur Compile-Zeit.

Lesson 1: Das Problem mit Laufzeit-Pruefungen

Stell dir vor eine Bestellung im Status VERSANDT soll storniert werden. Business-Regel: Stornieren geht nur bis zum Versand. Klassisches OO:

class Order {
  status: string;
  cancel() {
    if (this.status !== "PAID") throw new Error("Zu spaet");
    this.status = "CANCELLED";
  }
}
new Order({ status: "SHIPPED" }).cancel(); // Laufzeitfehler!

Der Fehler ist zur Laufzeit — Compilierung klappt. Tests koennen ihn finden, aber Tests decken nicht alle Pfade ab.

Lesson 2: Typestate — Zustand als Typ

Jeder Zustand wird ein eigener Typ. Uebergaenge sind Methoden die nur auf bestimmten Typen definierbar sind:

// Rust mit typestate-Crate
pub struct DraftOrder;
pub struct PaidOrder;
pub struct ShippedOrder;

impl DraftOrder {
    pub fn place(self, payment: Payment) -> PaidOrder { PaidOrder }
}
impl PaidOrder {
    pub fn ship(self, tracking: TrackingId) -> ShippedOrder { ShippedOrder }
    pub fn cancel(self) -> CancelledOrder { CancelledOrder }
}
// ShippedOrder hat KEIN cancel() — Compiler verbietet den Aufruf

Typ als Beweis: Wenn das Aggregat den Typ ShippedOrder hat, ist stornieren ein Compile-Error — nicht lauffaehig.

Lesson 3: Typestate trifft Event Sourcing

Event Sourcing (Modul 11) speichert jeden Uebergang als Event: OrderPlaced, PaymentReceived, Shipped. Typestate erzwingt Uebergaenge zur Compile-Zeit. Beide ergaenzen sich:

flowchart LR
  Command -- Typestate -->|erlaubt?| Event
  Event -- append --> EventStore
  EventStore -.->|Replay| Aggregat

Event Store ist die single source of truth. Typestate garantiert dass die Write Side nur gueltige Events erzeugt.

Lesson 4: Implementierung in Rust und TypeScript

Rust: typestate-Crate oder manuell mit PhantomData und generischen Parametern. TypeScript: discriminated unions — jeder Zustand hat ein Literal-Feld als Discriminator.

// TypeScript: Discriminated Union
type DraftOrder = { kind: "draft"; id: string };
type PaidOrder = { kind: "paid"; id: string; amount: number };
type ShippedOrder = { kind: "shipped"; id: string; tracking: string };

function ship(order: PaidOrder): ShippedOrder { return { kind: "shipped", ... };
function cancel(order: PaidOrder): CancelledOrder { return { kind: "cancelled", ... };
// ShippedOrder hat kein cancel() im Typ — TypeScript verbietet den Aufruf

Lesson 5: Audit plus Garantie

AspektTypestateEvent SourcingBeide
ErzwingtCompile-ZeitLaufzeitCompile + Laufzeit
AuditNeinJa — HistorieJa + sicher

Typestate ideal fuer Domänen mit festen Regeln und hohen Fehlerkosten.

Compile-Zeit-Sicherheit + Laufzeit-Audit

Typestate macht Business-Regeln unuebersehbar: Aufruf gültig nur wenn der Typ es erlaubt. Event Sourcing speichert jeden Aufruf als Beleg. Zusammen: Correcness by construction + Proof by history.

Praxis-Szenario: Die nicht-stornierbare Bestellung

Ein Team will verhindern dass Bestellungen im Status VERSANDT noch storniert werden. Wie modellierst du das mit Typestate + Event Sourcing damit der Fehler gar nicht kompilierbar ist?

✅ Wichtige Erkenntnisse

Haken setzen, um deinen Lernfortschritt zu markieren:

    This site uses essential cookies for Stripe payments. No tracking cookies.