Wenn du anfängst, dich intensiver mit Softwaredesign zu beschäftigen, stolperst du relativ früh über Prinzipien mit großen Namen. Eins davon ist Command–Query Separation, kurz CQS. Klingt erstmal theoretisch, ist aber im Alltag extrem praktisch - gerade dann, wenn dein Code langsam größer wird und du nicht mehr jede Methode im Kopf behalten kannst.
Die Grundidee von CQS ist simpel: Eine Methode soll entweder etwas verändern oder etwas zurückgeben. Beides zusammen ist tabu. Keine Magie, kein Framework, kein Pattern-Overkill. Einfach eine klare Regel, die dir hilft, sauberen und wartbaren Code zu schreiben.
Was bedeutet Command und was Query?
Ein Command ist eine Methode, die den Zustand deines Systems verändert. Sie schreibt in eine Datenbank, ändert ein Objekt oder löst irgendeinen Seiteneffekt aus. Dafür liefert sie keinen fachlichen Rückgabewert.
Eine Query ist das Gegenteil. Sie liefert Informationen zurück, verändert aber nichts. Kein Schreiben, kein Update, kein versteckter Seiteneffekt.
Die Regel von CQS lautet also: Eine Methode ist entweder Command oder Query - niemals beides.
Das klingt banal, wird aber erstaunlich oft verletzt, vor allem am Anfang.
Ein klassisches Gegenbeispiel
Schau dir mal diese Methode an:
public boolean login(String username, String password) {
User user = userRepository.findByUsername(username);
if (user == null) {
return false;
}
boolean valid = passwordService.check(password, user.getPasswordHash());
if (valid) {
user.setLastLogin(LocalDateTime.now());
userRepository.save(user);
}
return valid;
}
Auf den ersten Blick wirkt das ganz okay. Die Methode gibt zurück, ob der Login erfolgreich war. Gleichzeitig verändert sie aber den Zustand, indem sie lastLogin setzt und den User speichert.
Damit verletzt sie CQS. Sie ist Query und Command zugleich.
Das Problem daran ist nicht akademisch. Der Aufrufer der Methode sieht nur den Rückgabewert. Dass hier nebenbei Daten geändert werden, ist nicht offensichtlich. Später fragt sich jemand, warum bei einem scheinbar harmlosen Methodenaufruf plötzlich Daten in der Datenbank landen.
Warum das in der Praxis weh tut
Sobald Methoden mehr als eine Verantwortung haben, wird Code schwer vorhersehbar. Du kannst eine Methode nicht mehr bedenkenlos mehrfach aufrufen. Tests werden komplizierter, weil du neben dem Rückgabewert auch immer den Seiteneffekt im Blick behalten musst.
Gerade in JavaEE-Anwendungen mit JPA, Transaktionen und Caching führt das schnell zu subtilen Bugs. Ein Reload der Seite triggert plötzlich Logik, die eigentlich nur einmal laufen sollte. Oder ein Test schlägt fehl, weil plötzlich Daten persistiert werden, obwohl du nur etwas abfragen wolltest.
CQS verhindert genau solche Stolperfallen.
Die saubere Trennung nach CQS
Die Lösung ist meist einfach: trennen.
public boolean isLoginValid(String username, String password) {
User user = userRepository.findByUsername(username);
if (user == null) {
return false;
}
return passwordService.check(password, user.getPasswordHash());
}
public void updateLastLogin(String username) {
User user = userRepository.findByUsername(username);
if (user != null) {
user.setLastLogin(LocalDateTime.now());
userRepository.save(user);
}
}
Jetzt ist klar, was passiert. Die erste Methode fragt etwas ab. Die zweite verändert Zustand. Kein Rätselraten, keine Überraschungen.
Der aufrufende Code entscheidet bewusst, was wann passiert. Das macht den Ablauf transparenter und besser testbar.
Typische Stolperstellen für Anfänger
Eine häufige Situation ist das Speichern und Zurückgeben von Objekten:
public User saveUser(User user) {
return userRepository.save(user);
}
Auch das ist streng genommen eine CQS-Verletzung. Die Methode verändert Zustand und liefert etwas zurück. In der Praxis wird das oft toleriert, vor allem bei technischen Methoden auf Repository-Ebene.
Wichtig ist hier das Bewusstsein. Je näher du an der Fachlogik bist, desto strikter solltest du CQS einhalten. In Services und Controllern lohnt sich diese Disziplin fast immer.
CQS ist kein Dogma
CQS ist ein Prinzip, kein Gesetz. Es gibt legitime Ausnahmen, besonders in Infrastruktur-Code oder bei Framework-APIs, die du nicht kontrollierst.
Der eigentliche Mehrwert entsteht, wenn du CQS als Denkwerkzeug nutzt. Frag dich bei jeder Methode: Will ich hier etwas wissen oder etwas verändern?
Wenn beides zutrifft, ist das ein starkes Signal, die Methode aufzuteilen.
Bezug zu CQRS
CQS wird oft mit CQRS verwechselt oder gleichgesetzt. CQRS steht für Command Query Responsibility Segregation und geht deutlich weiter. Dort werden Lese- und Schreibmodelle komplett getrennt, oft mit eigenen Datenmodellen und sogar eigenen Datenbanken.
CQS ist der kleine, alltagstaugliche Bruder davon. Du kannst CQS anwenden, ohne dein gesamtes Architekturdesign umzubauen. Genau deshalb ist es für Anfänger so wertvoll.
Fazit
Command–Query Separation zwingt dich, klar über den Zweck deiner Methoden nachzudenken. Das Ergebnis ist Code, der leichter zu lesen, besser zu testen und weniger fehleranfällig ist.
Gerade am Anfang fühlt sich die Trennung manchmal unnötig an. Mit wachsender Codebasis merkst du aber schnell, wie viel Klarheit dadurch entsteht. CQS ist eines dieser Prinzipien, die leise im Hintergrund wirken – aber langfristig einen riesigen Unterschied machen.
