Zum Hauptinhalt springen
Alle Beiträge
Software Engineering4 Min. Lesezeit

Event Sourcing und CQRS in .NET 9: Wann sich die Komplexität lohnt

Ein praxisnaher Leitfaden zu Event Sourcing und CQRS in .NET 9 — wann das Muster seine Komplexität rechtfertigt, Implementierung mit Marten und Produktionserfahrungen.

Veröffentlicht

Event Sourcing und CQRS sind leistungsstarke Muster. Sie sind aber auch die am häufigsten übermäßig angewendeten Muster in Enterprise-Software. Teams übernehmen sie, weil sie elegant klingen, und verbringen dann Monate damit, gegen Infrastrukturkomplexität zu kämpfen, die eine einfache CRUD-Anwendung vermieden hätte.

Dieser Beitrag ist kein Evangelisierungs-Artikel. Er erklärt, wann Event Sourcing seinen Aufwand wirklich rechtfertigt, wie man es in .NET 9 mit Marten implementiert und welche Produktionsprobleme auftreten, die Blog-Tutorials nie erwähnen.

Wann sich Event Sourcing lohnt

Sie brauchen Event Sourcing wahrscheinlich wenn:

Vollständiger Audit Trail ist regulatorische Pflicht — Finanzdienste, Gesundheitswesen und Rechtssysteme müssen nachweisen, was genau wann und in welcher Reihenfolge passiert ist. Ein Append-only Event Log ist rechtlich belastbarer als veränderbare Datenbankzeilen.

Ihre Domäne hat komplexe Zustandsmaschinen — Ein Versicherungsanspruch, der durch Created → UnderReview → AdditionalInfoRequested → Approved → Paid → Disputed → Resolved wandert, hat Übergänge, die am besten als Events modelliert werden.

Sie brauchen temporale Abfragen — „Was war der Kontostand am 15. März?" Event Replay zu einem Zeitpunkt beantwortet das natürlich.

Mehrere Read-Models aus denselben Daten — Ein einzelner Event Stream kann gleichzeitig in eine relationale Berichtsdatenbank, einen Suchindex und ein Echtzeit-Dashboard projizieren.

Sie brauchen Event Sourcing wahrscheinlich nicht wenn:

  • Ihre Domäne einfaches CRUD ohne komplexe Zustandsübergänge ist
  • Sie starke Konsistenz für Lesevorgänge unmittelbar nach Schreibvorgängen brauchen
  • Ihr Team keine Erfahrung mit dem Muster hat und keine Zeit zum Lernen
  • Ihre Audit-Anforderungen mit einer einfachen Änderungsprotokoll-Tabelle erfüllt werden können

Architekturübersicht

Loading diagram...

Implementierung mit Marten in .NET 9

Marten verwendet PostgreSQL sowohl als Event Store als auch als Dokumentendatenbank.

Das Aggregate

Csharp
public sealed class ShoppingCart
{
    public Guid Id { get; private set; }
    public CartStatus Status { get; private set; }
    public List<CartItem> Items { get; private set; } = new();
    public decimal TotalAmount => Items.Sum(i => i.Price * i.Quantity);

    public void Apply(CartCreated @event)
    {
        Id = @event.CartId;
        Status = CartStatus.Active;
    }

    public void Apply(ItemAdded @event)
    {
        Items.Add(new CartItem(@event.ProductId, @event.ProductName, @event.Price, @event.Quantity));
    }

    public void Apply(CartCheckedOut @event)
    {
        Status = CartStatus.CheckedOut;
    }
}

Command Handler

Csharp
public sealed class AddItemHandler
{
    private readonly IDocumentSession _session;

    public async Task Handle(AddItemCommand command, CancellationToken ct)
    {
        var stream = await _session.Events.FetchForWriting<ShoppingCart>(command.CartId, ct);
        var cart = stream.Aggregate;

        if (cart.Status != CartStatus.Active)
            throw new InvalidOperationException("Artikel können nicht zu einem ausgecheckten Warenkorb hinzugefügt werden.");

        stream.AppendOne(new ItemAdded(
            command.CartId, command.ProductId, command.ProductName, command.Price, command.Quantity
        ));

        await _session.SaveChangesAsync(ct);
    }
}

Projektionen (Read Models)

Csharp
public sealed class CartSummaryProjection : SingleStreamProjection<CartSummary>
{
    public CartSummary Create(CartCreated @event) => new()
    {
        Id = @event.CartId,
        CustomerId = @event.CustomerId,
        CreatedAt = @event.Timestamp,
        ItemCount = 0,
        TotalAmount = 0
    };

    public CartSummary Apply(ItemAdded @event, CartSummary current) => current with
    {
        ItemCount = current.ItemCount + @event.Quantity,
        TotalAmount = current.TotalAmount + (@event.Price * @event.Quantity)
    };
}

Event-Lebenszyklus: Vom Command zum Read Model

Loading diagram...

Produktions-Herausforderungen

Snapshotting

10.000 Events zu replaying zum Aggregate-Rebuild ist langsam:

Csharp
opts.Events.UseAggregateSnapshots<ShoppingCart>(snapshotting =>
{
    snapshotting.SnapshotEvery(50);
});

Event-Versionierung und Upcasting

Csharp
public sealed class OrderCreatedV1ToV2Upcaster : EventUpcaster<OrderCreatedV1, OrderCreatedV2>
{
    protected override OrderCreatedV2 Upcast(OrderCreatedV1 old) => new(
        old.OrderId, old.CustomerId, Currency: "EUR"
    );
}

Eventual Consistency in Read Models

Asynchrone Projektionen bedeuten, dass Abfragen veraltete Daten zurückgeben können. Lösungen:

  • Event-Version mit Schreibvorgängen zurückgeben, Client pollt bis Read Model aufholt
  • Inline-Projektionen für kritische Read Models verwenden
  • UI optimistisch gestalten

Kosten-Nutzen-Zusammenfassung

NutzenKosten
Vollständiger Audit TrailEvent-Versionierung-Komplexität
Temporale AbfragenProjektions-Rebuild-Zeit
Mehrere Read ModelsEventual Consistency
Natürliche DomänenmodellierungTeam-Lernkurve
Produktionsprobleme debuggenSpeicherwachstum (Append-only)

Die Komplexität ist real. Aber für die richtigen Domänen — Finanztransaktionen, Versicherungsansprüche, Logistik-Workflows, Compliance-schwere Systeme — transformiert Event Sourcing unmögliches Debugging in triviales Event Replay.


Erwägen Sie Event Sourcing für Ihr Enterprise-System? Kontaktieren Sie uns — wir helfen Teams, das Muster dort einzusetzen, wo es echten Mehrwert bringt.

Themen

Event Sourcing .NETCQRS-MusterMarten EventStoreDomain-Driven DesignEnterprise-Architekturmuster

Häufig gestellte Fragen

Event Sourcing lohnt sich bei regulatorischer Audit-Pflicht (Finanzsysteme, Gesundheitswesen), bei komplexen Zustandsübergängen, bei temporalen Abfragen (Zustand zum Zeitpunkt T) oder wenn mehrere Read-Models aus denselben Daten benötigt werden.

Expert engagement

Brauchen Sie Expertenberatung?

Unser Team ist spezialisiert auf Cloud-Architektur, Security, KI-Plattformen und DevSecOps. Lassen Sie uns besprechen, wie wir Ihrem Unternehmen helfen können.

Kontakt aufnehmenNo commitment · No sales pressure

Verwandte Artikel

Alle Beiträge