<EbeneX/>
Architektur Architektur · Updated 1. Juli 2026

CQRS

Definition

Ein Architektur-Pattern, das Lese- und Schreiboperationen trennt – unterschiedliche Modelle für Commands (Änderungen) und Queries (Abfragen).

Experte 3 Min. Lesezeit EN: Command Query Responsibility Segregation

Einfach erklärt

CQRS trennt deine Anwendung in zwei Teile: Einen für Änderungen (Commands) und einen für Abfragen (Queries). Jeder Teil kann unabhängig optimiert werden.

Traditionell (ein Modell für alles):

┌─────────────────────────────────┐
│           Application           │
│  ┌─────────────────────────┐   │
│  │      Single Model       │   │
│  │  (Read + Write)         │   │
│  └─────────────────────────┘   │
│              ↓                  │
│  ┌─────────────────────────┐   │
│  │       Database          │   │
│  └─────────────────────────┘   │
└─────────────────────────────────┘

CQRS (getrennte Modelle):

┌─────────────────────────────────────────────┐
│                Application                   │
│  ┌─────────────┐     ┌─────────────┐        │
│  │  Commands   │     │   Queries   │        │
│  │  (Write)    │     │   (Read)    │        │
│  └──────┬──────┘     └──────┬──────┘        │
│         ↓                   ↓               │
│  ┌─────────────┐     ┌─────────────┐        │
│  │ Write DB    │ ──→ │  Read DB    │        │
│  │ (normalized)│     │(denormalized)│       │
│  └─────────────┘     └─────────────┘        │
└─────────────────────────────────────────────┘

Warum trennen?

AspektWrite-SeiteRead-Seite
OptimierungKonsistenz, Validierungschnelle und passende Abfragen
Schemaoft stärker normalisiertoft denormalisiert oder view-orientiert
Skalierungabhängig von Schreiblastabhängig von Leselast
KomplexitätBusiness-LogikUI-, Reporting- oder API-nahe Views

Technischer Deep Dive

Commands und Queries

Command (ändert Zustand):

@dataclass
class CreateOrderCommand:
    customer_id: str
    items: List[OrderItem]

@dataclass
class AddItemCommand:
    order_id: str
    sku: str
    quantity: int

class CommandHandler:
    def handle_create_order(self, cmd: CreateOrderCommand):
        order = Order.create(cmd.customer_id, cmd.items)
        self.repository.save(order)
        # Kein Return-Wert (oder nur ID)

Query (liest Zustand):

@dataclass
class GetOrderQuery:
    order_id: str

@dataclass
class ListOrdersQuery:
    customer_id: str
    status: Optional[str] = None

class QueryHandler:
    def handle_get_order(self, query: GetOrderQuery) -> OrderDTO:
        return self.read_db.get_order(query.order_id)
    
    def handle_list_orders(self, query: ListOrdersQuery) -> List[OrderDTO]:
        return self.read_db.find_orders(
            customer_id=query.customer_id,
            status=query.status
        )

Synchronisation Write → Read

Option 1: Synchron (einfacher, aber stärker gekoppelt):

class OrderService:
    def create_order(self, cmd: CreateOrderCommand):
        # Write
        order = Order.create(cmd.customer_id, cmd.items)
        self.write_db.save(order)
        
        # Sync to Read (im selben Request)
        self.read_db.upsert_order_view(order.to_view())

Option 2: Asynchron via Events (entkoppelter, aber eventual consistent):

class OrderService:
    def create_order(self, cmd: CreateOrderCommand):
        order = Order.create(cmd.customer_id, cmd.items)
        self.write_db.save(order)
        
        # Event publizieren
        self.event_bus.publish(OrderCreatedEvent(order))

class OrderViewUpdater:
    @subscribe(OrderCreatedEvent)
    def handle(self, event: OrderCreatedEvent):
        # Read-Modell asynchron aktualisieren
        self.read_db.upsert_order_view(event.order.to_view())

Read-Modell optimieren

Write-Modell (normalisiert):

-- Mehrere Tabellen, Joins nötig
orders (id, customer_id, status, created_at)
order_items (id, order_id, product_id, quantity, price)
products (id, name, description)
customers (id, name, email)

Read-Modell (denormalisiert):

-- Eine Tabelle, alles drin
order_views (
    id,
    customer_name,
    customer_email,
    status,
    items_json,  -- [{name, quantity, price}, ...]
    total,
    created_at
)

Eventual Consistency

Timeline:
─────────────────────────────────────────────────→
    │                    │                    │
    ▼                    ▼                    ▼
  Write              Event              Read Updated
                    Published
    
    └────── mögliche Inconsistency Window ──────┘

Umgang damit:

  • UI zeigt optimistisch den erwarteten Zustand
  • Polling oder WebSocket für Updates
  • Für kritische Reads: Direkt aus Write-DB lesen

Wann CQRS?

SzenarioCQRS sinnvoll?
Einfaches CRUDmeist nicht nötig
Deutlich andere Read-/Write-Lastkann separate Skalierung ermöglichen
Komplexe Reportskann denormalisierte Views vereinfachen
Event Sourcinghäufig passende Ergänzung
Strenge Echtzeit- oder KonsistenzanforderungenEventual Consistency sorgfältig prüfen

CQRS ist wie ein Restaurant mit getrennter Küche und Ausgabe: Die Küche (Write) ist optimiert für Zubereitung, die Ausgabe (Read) für schnelles Servieren. Beide haben unterschiedliche Anforderungen und Optimierungen.

Trennt Commands (Schreiben) von Queries (Lesen)

Ermöglicht unterschiedliche Optimierungen für Read und Write

Oft kombiniert mit Event Sourcing

High-Read-Systeme

Viele Leser, wenige Schreiber – Read-Seite separat skalieren

Komplexe Domains

Write-Modell für Business-Logik, Read-Modell für UI

Reporting

Denormalisierte Views für schnelle Reports

Event Sourcing

Events schreiben, Projections für Queries

Wann brauche ich CQRS?

Bei stark unterschiedlichen Read/Write-Anforderungen, komplexer Domain-Logik, Reporting-Bedarf oder gezielter Skalierung. Für einfache CRUD-Apps ist der zusätzliche Aufwand meist nicht gerechtfertigt.

Ist CQRS dasselbe wie Event Sourcing?

Nein, aber sie passen gut zusammen. CQRS trennt Read/Write. Event Sourcing speichert Events statt Zustand. Man kann CQRS ohne Event Sourcing nutzen und umgekehrt.

Was ist Eventual Consistency bei CQRS?

Read-Modelle können asynchron aktualisiert werden. Nach einem Write kann das Read-Modell daher kurzzeitig veraltet sein. Ob das akzeptabel ist, hängt vom Use Case und Konsistenzbedarf ab.

Erhöht CQRS die Komplexität?

Ja, meist. Zwei Modelle, Synchronisation und mögliche Eventual Consistency erhöhen Architektur- und Betriebsaufwand. CQRS sollte nur eingesetzt werden, wenn die Vorteile diesen Aufwand rechtfertigen.

Dein persönliches Share-Bild für Instagram – 1080×1080px, bereit zum Posten.