NCA Social Media
Aktualisiert:
Autor:
Roland Golla
Grüner Weg teilt sich am Wegweiser CQRS in Schreiben und Lesen

CQRS: Definition und Einordnung

CQRS steht für Command Query Responsibility Segregation und trennt schreibende von lesenden Zugriffen. Commands ändern den Zustand und geben nichts zurück, Queries liefern Daten und ändern nichts.
Der Nutzen entsteht dort, wo beide Seiten unterschiedliche Anforderungen haben. Eine Bestellung wird beim Schreiben streng geprüft, jede Regel muss halten. Dieselbe Bestellung erscheint in einer Übersicht neben zwanzig anderen, dort zählt nur Geschwindigkeit. Ein einziges Modell für beides wird am Ende für keine der beiden Seiten gut.
CQRS gehört zum taktischen Werkzeugkasten rund um Domain Driven Design und wird meistens innerhalb eines Bounded Context eingesetzt, nicht im gesamten System. Es ist kein Architekturstil, sondern eine Entscheidung pro Bereich.

CQRS mit NCA: Schnelle Hilfe vom Experten

CQRS wird oft zu früh und zu großflächig eingeführt. Never Code Alone arbeitet seit über zwanzig Jahren an PHP Systemen und hilft bei der ehrlichen Abwägung: Wo trägt die Trennung, wo kostet sie nur. Unsere Basis ist täglicher Betrieb mit Symfony, Sulu CMS, PHPUnit und PHPStan.
Vor jeder Umstellung steht die Absicherung mit Characterization Tests, danach folgen Umbauten mit Parallel Change und der Vergleich alter und neuer Ergebnisse mit Parallel Run. Die Schichtgrenzen prüft Deptrac. Wie KI Agents mit getrennten Modellen umgehen, ordnen wir bei Vibe Coding Consulting ein.
Schreiben und Lesen sauber trennen mit NCA
Finde das passende Angebot für dein Projekt
Anfrage-Konfiguration
Starten Sie Ihre Anfrage
Projektart
Infos
Nachricht

Eliminierung technischer Schulden mit PHPStan, Rector PHP und PHPUnit. Über 20 Jahre Praxiserfahrung in skalierbaren Backends.

CORE EXPERTISE

Gesetzliche Konformität & Inklusion. Optimierung von Performance und Conversion durch radikal nutzerzentriertes, universelles Design.

BFSG COMPLIANT

Skalierbare KI-Systeme mit echtem Code Ownership. CI/CD, Backup-Strategien und Infrastruktur, die mit deinem Team wächst.

ENTERPRISE READY

Command und Query im Alltag unterscheiden

Die Trennung folgt einer einfachen Regel: Ein Command sagt, was passieren soll, und gibt keinen Zustand zurück. Eine Query fragt Daten ab und verändert nichts. Wer beides mischt, bekommt Methoden, deren Aufruf nebenbei etwas ändert, und genau dort entstehen die Fehler, die niemand im Review sieht.
  • Command: CancelOrder, IncreaseDunningLevel, RecordPayment. Formuliert als Absicht, nicht als Datensatz.
  • Query: OpenOrdersOfCustomer, RevenueOverview. Liefert genau die Form, die die Oberfläche braucht.
  • Handler: Jede Absicht und jede Abfrage bekommt eine eigene kleine Klasse statt einer Service Klasse mit zwanzig Methoden.
Schon diese Trennung bringt Nutzen, ganz ohne zweite Datenbank. Die Lesemodelle dürfen direkt mit SQL arbeiten und genau die Spalten holen, die gebraucht werden. Das Schreibmodell bleibt fachlich streng und muss keine Rücksicht auf Listenansichten nehmen.

CQRS in PHP mit Symfony Messenger

Symfony bringt alles mit, was für CQRS nötig ist. Der Messenger kennt getrennte Busse, sodass Commands und Queries unterschiedlich behandelt werden können, etwa synchron beim Lesen und asynchron beim Schreiben.
Code:
          

final readonly class CancelOrder
{
    public function __construct(
        public OrderId $orderId,
        public string $reason,
    ) {
    }
}

#[AsMessageHandler(bus: 'command.bus')]
final readonly class CancelOrderHandler
{
    public function __construct(
        private Orders $orders,
    ) {
    }

    public function __invoke(CancelOrder $command): void
    {
        $order = $this->orders->withId($command->orderId);
        $order->cancel(new CancellationReason($command->reason));

        $this->orders->save($order);
    }
}

Die Leseseite braucht dieses Modell nicht. Sie fragt die Daten in der Form ab, die die Oberfläche anzeigt, und umgeht dabei bewusst das Domänenmodell.
Code:
          

final readonly class OpenOrdersHandler
{
    public function __construct(
        private Connection $connection,
    ) {
    }

    public function __invoke(OpenOrders $query): array
    {
        return $this->connection->fetchAllAssociative(
            'SELECT number, customer, total_cents FROM orders WHERE status = :status',
            ['status' => 'open'],
        );
    }
}

Zwei Busse in der Konfiguration halten die Trennung sichtbar. Deptrac stellt sicher, dass die Leseseite nicht doch wieder auf Domänenklassen zugreift, und PHPStan fängt falsche Typen früh ab.
Code:
          

framework:
  messenger:
    default_bus: command.bus
    buses:
      command.bus: ~
      query.bus:
        middleware:
          - validation

CQRS in Stufen statt als Alles oder Nichts

Stufe Was getrennt wird Preis dafür
Stufe 1 Methoden: Commands ändern, Queries lesen Fast keiner, reine Disziplin im Code
Stufe 2 Eigene Handler je Absicht und Abfrage Mehr Klassen, dafür kleine und testbare Einheiten
Stufe 3 Eigene Lesemodelle neben dem Domänenmodell Zwei Modelle müssen gepflegt werden
Stufe 4 Eigener Lesespeicher, asynchron aktualisiert Eventual Consistency und deutlich mehr Betrieb

Wann CQRS trägt und wann es nur kostet

Die meisten Teams brauchen Stufe eins und zwei. Getrennte Absichten und kleine Handler machen Code lesbar und Tests kurz, ohne dass jemand Eventual Consistency erklären muss.
Lohnt sich: komplexe Schreibregeln neben vielen unterschiedlichen Auswertungen, stark abweichende Last auf beiden Seiten, Berichte, die das Domänenmodell verbiegen würden.
Lohnt sich nicht: einfache Verwaltungsanwendungen, kleine Teams ohne Erfahrung mit asynchroner Verarbeitung, Systeme ohne Monitoring. Ein eigener Lesespeicher ohne Messwerte und ohne Alarmierung wird zur Blackbox, sobald die Aktualisierung einmal hängt. Sentry und Grafana gehören deshalb vor die Umstellung, nicht danach.
Ein häufiges Missverständnis: CQRS verlangt kein Event Sourcing. Beides passt gut zusammen, funktioniert aber unabhängig voneinander. Wer beides gleichzeitig einführt, verdoppelt das Risiko ohne Not.

CQRS is not an architecture.

Greg Young, Urheber des CQRS Begriffs – Greg Young's Blog

Die NCA Erfahrung mit getrennten Modellen

In der Praxis beginnt CQRS fast immer mit einem Performanceproblem in einer Übersicht. Bevor umgebaut wird, messen wir: Xdebug und SPX zeigen, wo die Zeit hingeht, ein Flame Graph macht es sichtbar. Oft reicht schon eine eigene Abfrage statt einer Umstellung der gesamten Architektur.
Wird umgebaut, läuft es abgesichert. Characterization Tests halten das Verhalten fest, Symfony KernelTestCase prüft die Handler im Container, Parallel Run vergleicht alte und neue Ergebnisse im laufenden Betrieb, bevor umgeschaltet wird.
Die fachliche Grundlage bleibt entscheidend. Ohne saubere Ubiquitous Language heißen Commands am Ende doch wieder update und save. Wie die Bereiche zusammenhängen, zeigt eine Karte aus dem Context Mapping. Weitere Werkzeuge stehen im NCA PHP Glossar.
CYPRESS.IO Ambassador und IT Consultant für QA Engenieering und Qualität in PHP Projekten.

Erreichen Sie unsere PHP Consultant Spezialisten

Wir sind Experten für PHP und helfen Ihnen, Ihre digitalen Herausforderungen zu meistern. Unser erfahrenes Team unterstützt Sie bei PHP Updates, PHP Refactoring und berät Sie remote zu allen Fragen rund um PHP. Mit unseren vollautomatischen CI/CD Deployments und einer robusten Docker-Infrastruktur bringen wir Ihre PHP-Projekte auf das nächste Level. Vertrauen Sie auf unsere Expertise für zuverlässige und skalierbare PHP-Lösungen.

Häufige Fragen zu CQRS

Die Fragen, die bei CQRS in Reviews und Workshops immer wieder gestellt werden.

Was ist CQRS 2026 einfach erklärt?

CQRS steht für Command Query Responsibility Segregation und trennt schreibende von lesenden Zugriffen. Ein Command beschreibt eine Absicht, ändert Zustand und gibt nichts zurück. Eine Query liefert Daten und ändert nichts. Dadurch darf die Schreibseite fachlich streng sein, während die Leseseite genau die Form liefert, die die Oberfläche braucht.

Braucht CQRS 2026 zwingend Event Sourcing?

Nein. Beides wird oft gemeinsam genannt, funktioniert aber unabhängig voneinander. CQRS lässt sich mit einer einzigen relationalen Datenbank umsetzen, indem Lesezugriffe direkt per SQL laufen und Schreibzugriffe über das Domänenmodell. Wer beides gleichzeitig einführt, verdoppelt das Risiko ohne Notwendigkeit.

Wie setzt man CQRS 2026 in Symfony um?

Über den Messenger mit zwei getrennten Bussen. Commands laufen über den Command Bus zu einem Handler je Absicht, Queries über den Query Bus zu einem Handler je Abfrage. Die Konfiguration ist kurz, der eigentliche Gewinn liegt in kleinen Klassen mit klarem Zweck statt einer Service Klasse mit zwanzig Methoden.

Lohnt sich CQRS 2026 für kleine Projekte?

Nur in der einfachsten Stufe. Die Trennung von ändernden und lesenden Methoden kostet nichts und verbessert jeden Code. Eigene Lesemodelle oder gar ein eigener Lesespeicher lohnen sich erst, wenn Schreibregeln komplex sind oder die Leselast deutlich abweicht. Für eine Verwaltungsanwendung ist das Overkill.

Welche Rolle spielt CQRS 2026 bei KI gestützter Entwicklung?

Kleine Handler mit klarem Zweck sind für einen Agenten deutlich leichter zu ändern als eine gewachsene Service Klasse. Jede Absicht hat eine eigene Datei, einen eigenen Test und einen eindeutigen Namen. Damit bleibt der Ausschnitt klein, den ein Modell verstehen und zuverlässig anfassen kann.

Was ist der Unterschied zwischen CQS und CQRS?

CQS ist das ältere Prinzip auf Methodenebene: Eine Methode ändert entweder etwas oder liefert etwas zurück, nie beides. CQRS überträgt diesen Gedanken auf die Ebene der Modelle und erlaubt getrennte Modelle für Schreiben und Lesen. CQS gilt überall, CQRS ist eine bewusste Entscheidung pro Bereich.

Was bedeutet Eventual Consistency in diesem Zusammenhang?

Wird die Leseseite asynchron aktualisiert, sieht ein Nutzer seine Änderung eventuell erst Sekundenbruchteile später. Fachlich muss das in Ordnung sein. Bei einer Statistik ist es unkritisch, bei einer Buchung mit sofortiger Bestätigung nicht. Diese Frage gehört vor die technische Entscheidung, nicht danach.

Wo liegt der häufigste Fehler bei CQRS?

Die flächendeckende Einführung im gesamten System. CQRS ist eine Entscheidung pro Bounded Context und oft nur für einzelne Bereiche sinnvoll. Zweiter häufiger Fehler sind Commands, die wie Datenbankoperationen heißen. Heißt ein Command update, ist die fachliche Absicht bereits verloren gegangen.

Dürfen Queries direkt auf die Datenbank zugreifen?

Ja, das ist sogar ein Kernpunkt. Die Leseseite braucht das Domänenmodell nicht und darf mit einfachem SQL genau die Spalten holen, die angezeigt werden. Wichtig ist nur, dass sie nichts ändert und dass dieser Zugriff technisch abgesichert bleibt, etwa über Deptrac Regeln.

Wie testet man Commands und Queries?

Commands über die Fachregeln: Ein Handler bekommt ein Command, danach wird geprüft, ob das Aggregate den erwarteten Zustand hat. Queries über das Ergebnis: Testdaten anlegen, Abfrage ausführen, Struktur prüfen. Beides läuft mit PHPUnit, für das Zusammenspiel im Container hilft Symfony KernelTestCase.

Lässt sich CQRS nachträglich einführen?

Ja, schrittweise und pro Anwendungsfall. Üblich ist der Start bei einer langsamen Übersicht, die eine eigene Abfrage bekommt. Danach folgen weitere Bereiche. Vorher sollte das aktuelle Verhalten mit Tests festgehalten werden, damit die Umstellung vergleichbar bleibt.

Wie unterstützt NCA bei der Einführung?

Wir messen zuerst, wo das Problem wirklich liegt, und prüfen, ob eine einfachere Lösung reicht. Ist CQRS der richtige Weg, bauen wir es abgesichert mit Tests und Parallel Run um. Jede Zusammenarbeit beginnt mit einem kostenlosen Kennenlernen, danach schätzen wir den Aufwand ein und rechnen transparent minutengenau ab.