CQRS und Event Sourcing in Rust: Architektur für kompromisslose Leistung

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Warum CQRS und Event Sourcing kein „Overkill“, sondern eine Rettung für komplexe Systeme sind

\n\n

Haben Sie jemals die Situation erlebt, dass Ihr in Rust geschriebenes System Leseabfragen perfekt verarbeitet, aber beim Schreiben „in die Knie geht“? Oder umgekehrt – Sie opfern die Leseleistung, um die vollständige Änderungshistorie speichern zu können? Kommt Ihnen das bekannt vor? Dann willkommen in der Welt von CQRS und Event Sourcing.

\n\n

Viele Entwickler betrachten diese Muster als „schweres Geschütz“, das nur in Enterprise-Projekten mit C# oder Java anwendbar ist. Aber mit der wachsenden Beliebtheit von Rust in hochbelasteten Systemen (von Spieleservern bis zu Finanzplattformen) werden CQRS und Event Sourcing nicht nur nützlich – sie werden notwendig. Rust mit seinen kostenlosen Abstraktionen und strengen Typprüfungen ist die ideale Sprache für die Implementierung dieser Muster.

\n\n

In diesem Artikel werden wir nicht nur über die Theorie sprechen. Wir werden funktionierenden Code in Rust schreiben, den Sie an Ihre Projekte anpassen können. Sie erfahren, wie Sie die Verantwortung zwischen Befehlen und Abfragen aufteilen, wie Sie Ereignisse anstelle des aktuellen Zustands speichern und wie all dies Ihr System um ein Vielfaches beschleunigt.

\n\n

Was sind CQRS und Event Sourcing? Einfach erklärt für Rust-Entwickler

\n\n

Stellen Sie sich vor, Sie verwalten ein Bankkonto. Im traditionellen CRUD-Modell haben Sie eine Tabelle „Konten“, in der der aktuelle Saldo gespeichert ist. Wenn Sie den Verlauf einsehen möchten, erstellen Sie eine weitere Tabelle „Transaktionen“. Aber was ist, wenn Sie nicht nur den Saldo kennen, sondern verstehen müssen, warum er sich geändert hat? Oder den Zustand zu einem beliebigen Datum wiederherstellen möchten?

\n\n

CQRS: Teile und herrsche

\n\n

CQRS (Command Query Responsibility Segregation) ist ein Prinzip, das Operationen in zwei Gruppen unterteilt:

\n
    \n
  • Befehle (Commands) – ändern den Zustand. Sie geben keine Daten zurück, nur eine Bestätigung oder einen Fehler.
  • \n
  • Abfragen (Queries) – lesen nur Daten. Sie ändern den Zustand nicht.
  • \n
\n\n

In Rust wirkt diese Trennung natürlich: Die Typen für Befehle und Abfragen sind unterschiedliche Strukturen, und ihre Handler sind unterschiedliche Funktionen oder Module. Dies ist nicht nur eine „gute Praxis“, sondern eine Möglichkeit, versehentliche Mutationen beim Lesen zu vermeiden.

\n\n
// Befehl – ändert den Zustand\npub struct CreateOrder {\n    pub user_id: Uuid,\n    pub items: Vec<OrderItem>,\n}\n\n// Abfrage – liest nur\npub struct GetOrderHistory {\n    pub user_id: Uuid,\n    pub limit: usize,\n}
\n\n

Event Sourcing: Speichern Sie die Geschichte, nicht die Momentaufnahme

\n\n

Event Sourcing ist ein Ansatz, bei dem wir nicht den aktuellen Zustand eines Objekts speichern, sondern die Abfolge von Ereignissen, die zu diesem Zustand geführt haben. Möchten Sie den Kontostand zum 1. Januar 2023 wissen? „Spielen“ Sie einfach alle Ereignisse von Anfang bis zu diesem Datum ab.

\n\n

Die Vorteile liegen auf der Hand:

\n
    \n
  • Prüfung (Audit) – jede Änderung ist dokumentiert.
  • \n
  • Zeitliche Schnappschüsse – jeder Zustand kann wiederhergestellt werden.
  • \n
  • Fehlertoleranz – wenn ein Fehler im Code vorliegt, haben Sie die Daten nicht verloren, sondern sie nur falsch „abgespielt“.
  • \n
\n\n

Praktisches Beispiel: Auftragsverwaltungssystem in Rust

\n\n

Lassen Sie uns ein Minisystem für einen Online-Shop erstellen. Es wird Befehle (Auftragserstellung, Artikel hinzufügen) und Abfragen (Warenkorb abrufen, Auftragshistorie) verarbeiten. Die gesamte Logik basiert auf Ereignissen.

\n\n

Schritt 1: Ereignisse und Befehle definieren

\n\n

Das Erste, was zu tun ist, ist die Domäne zu entwerfen. In Rust verwenden wir Aufzählungen (enum) für Ereignisse und strenge Strukturen für Befehle.

\n\n
use serde::{Deserialize, Serialize};\nuse uuid::Uuid;\n\n// Ereignisse – unveränderliche Fakten\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub enum OrderEvent {\n    Created { order_id: Uuid, user_id: Uuid, timestamp: i64 },\n    ItemAdded { order_id: Uuid, item: Str                

Blogs

Buchempfehlungen