CQRS
Odczyty i zapisy mają różne wymagania — dlaczego używać jednego modelu? CQRS rozdziela je na niezależne strony, które można skalować i optymalizować osobno.
3 strony architektury CQRS
CQRS dzieli system na trzy wyraźnie rozdzielone warstwy: zapis, odczyt i synchronizację między nimi.
Command Side (Write)
Obsługuje zapisy, egzekwuje reguły biznesowe, gwarantuje spójność
- →Command — intencja zmiany: CreateOrder, CancelOrder
- →Command Handler — walidacja i logika biznesowa
- →Domain Model (Aggregates) — reguły biznesowe
- →Write Store — PostgreSQL, Event Store
Query Side (Read)
Obsługuje odczyty, zoptymalizowane widoki, eventual consistency
- →Query — żądanie danych: GetOrderDetails, ListUserOrders
- →Query Handler — prosta operacja odczytu
- →Read Models — denormalizowane projekcje
- →Read Store — Elasticsearch, Redis, MongoDB, PostgreSQL
Synchronizacja (Event Bus)
Propaguje zmiany z write side do read side asynchronicznie
- →Events — fakty o zmianach stanu
- →Event Handlers (Projections) — aktualizują read models
- →Message Broker — Kafka, RabbitMQ, SQS
- →Dead Letter Queue — obsługa błędów projekcji
CQRS vs. CRUD — porównanie
CQRS nie zawsze jest lepszy — tylko w określonych kontekstach.
| Aspekt | CRUD | CQRS |
|---|---|---|
| Złożoność kodu | Niska — jeden model | Wysoka — dwa modele + synchronizacja |
| Wydajność odczytu | Ograniczona przez JOINy | Bardzo wysoka — pre-computed views |
| Skalowalność | Razem (write + read) | Niezależna dla write i read |
| Audit trail | Brak (tylko aktualny stan) | +ES: pełna historia zdarzeń |
| Czas wdrożenia | Krótki | Długi (2-4x dłużej) |
| Eventual consistency | Brak problemu | Wyzwanie — read lag |
Często zadawane pytania
Co to jest CQRS?
CQRS (Command Query Responsibility Segregation) to wzorzec architektoniczny który rozdziela operacje zapisu (Commands) od operacji odczytu (Queries) na oddzielne modele — i często na oddzielne bazy danych. Tradycyjny CRUD: jeden model i jedna baza do wszystkiego. Problem: odczyty i zapisy mają różne wymagania wydajnościowe i złożoności. CQRS rozdziela: Command side — obsługuje zapisy/modyfikacje. Walidacja, logika biznesowa, zapis do write store (baza relacyjna, event store). Query side — obsługuje odczyty. Zoptymalizowane modele danych (denormalizowane), szybkie odczyty, brak JOINów. Dlaczego CQRS: Skalowalność oddzielnie — skaluj czytelnicy niezależnie od piszących. Optymalizacja — write model normalizowany dla integralności, read model denormalizowany dla szybkości. Event Sourcing naturalnie łączy się z CQRS — komendy generują zdarzenia, zdarzenia budują projekcje (read models). CQRS to nie zawsze konieczność — dla prostych CRUD aplikacji jest over-engineering.
Jak działa Command side w CQRS?
Command side (strona zapisu) w CQRS: Command — intencja zmiany stanu: CreateOrder, CancelOrder, UpdateUserProfile. Command jest walidowany zanim zostanie przetworzony. Command Handler — odbiera komendę, waliduje, wykonuje logikę biznesową, zapisuje do write store lub emituje zdarzenia. Domain Model — agregaty z logiką biznesową (Domain-Driven Design). Aggregate = consistency boundary. Write Store — baza danych dla zapisów. Może być: relacyjna (PostgreSQL), document store (MongoDB) lub Event Store (EventStoreDB, Kafka). Ważne zasady Command side: Idempotentność — ta sama komenda wysłana 2 razy = ten sam wynik (używaj idempotency keys). Validation first — waliduj komendę przed jakimkolwiek zapisem. Thin controller, fat domain — logika w domain model, nie w handler. Command nie zwraca danych — zwraca sukces/błąd, nie obiekt (to byłoby Query). Narzędzia: MediatR (.NET), Axon Framework (Java), command bus pattern.
Jak działają Query side i Read Models?
Query side (strona odczytu) w CQRS: Read Models (projekcje) — denormalizowane widoki danych zoptymalizowane pod konkretne zapytania. Zamiast JOINowania 5 tabel, czytasz gotową, pre-computed projekcję. Jak są budowane: Event handlers słuchają zdarzeń z write side i aktualizują read store. Read Store — może być inny niż write store! Elasticseach dla full-text search, Redis dla cache i liczników, PostgreSQL z denormalizowanymi tabelami, MongoDB dla dokumentowych widoków. Query Handler — prosta operacja: pobierz z read store, zwróć DTO. Zero logiki biznesowej. Eventual consistency — read model jest za write modelem o milisekundy/sekundy. Musisz to zaakceptować lub obsłużyć (np. po zapisie zwróć ostatni stan z write store zamiast query side przez N sekund). Przykład: e-commerce. Write store: znormalizowane tabele orders/items/products. Read model: jeden dokument per zamówienie z wszystkimi danymi — OrderDetailsView. Zapytanie = proste ID lookup.
CQRS vs. tradycyjny CRUD — kiedy co stosować?
CQRS vs. CRUD: CRUD (Create, Read, Update, Delete) — jeden model do wszystkiego. Prostota, szybkość implementacji, łatwość zrozumienia. Sprawdza się dla prostych domenowych problemów gdzie odczyty i zapisy mają podobną złożoność. Kiedy NIE używaj CQRS: Prosta aplikacja CRUD (admin panele, proste API). Mały team bez doświadczenia z CQRS. MVP/startup w early stage — przedwczesna optymalizacja. Kiedy CQRS ma sens: Wymagania na odczyty i zapisy dramatycznie się różnią (np. setki queries/s vs. kilka commands/s). Złożona logika domenowa po stronie zapisu. Potrzebujesz różnych optymalizacji dla różnych widoków tych samych danych. Budujesz z Event Sourcing — CQRS jest naturalny. Systemy wymagające audit trail. Koszty CQRS: Zwiększona złożoność kodu. Eventual consistency — nowe wyzwanie dla developerów. Dwie bazy danych do zarządzania i synchronizacji. Reguła: nie stosuj CQRS na starcie — dodaj gdy outgrowth CRUD staje się bolesny.
CQRS z Event Sourcing — jak połączyć?
CQRS + Event Sourcing (ES) to naturalne połączenie i jeden z najpopularniejszych wzorców w systemach złożonych. Jak to działa razem: Command przychodzi do Command Handler. Handler waliduje i tworzy zdarzenia (OrderCreated, ItemAdded, PaymentProcessed). Zdarzenia są zapisywane do Event Store (append-only). Event Store publikuje zdarzenia do message broker. Event Handlers (projekcje) odbierają zdarzenia i aktualizują Read Models. Queries czytają z Read Models — błyskawiczne odczyty. Zalety połączenia: Event Store = write side (CQRS). Replay zdarzeń = rebuilding read models. Dodajesz nowy Read Model bez migracji danych — replayujesz zdarzenia. Pełen audit trail. Time travel debugging. Wady połączenia: Duża złożoność. Eventual consistency. Schema evolution zdarzeń. Frameworki wspierające CQRS+ES: Axon Framework (Java), EventSauce (PHP), NServiceBus (.NET), custom implementacje z Kafka + PostgreSQL.
Powiązane artykuły
Skontaktuj się z nami
Porozmawiajmy o Twoim projekcie. Bezpłatna wycena w ciągu 24 godzin.
Wyślij zapytanie
Telefon
+48 790 814 814
Pon-Pt: 9:00 - 18:00
adam@fotz.pl
Odpowiadamy w ciągu 24h
Adres
Plac Wolności 16
61-739 Poznań
Godziny pracy
Wolisz porozmawiać?
Zadzwoń teraz i porozmawiaj z naszym specjalistą o Twoim projekcie.
Zadzwoń teraz