DEV Community

Cover image for CQRS: separare lettura e scrittura per scalare e semplificare
Dev-Iadicola
Dev-Iadicola

Posted on • Originally published at iadicola.it

CQRS: separare lettura e scrittura per scalare e semplificare

Il problema: un modello che fa tutto

In un e-commerce, il modello Order serve sia per creare/modificare ordini (scrittura) sia per visualizzare la lista ordini, i report e le dashboard (lettura). Ma le esigenze sono opposte: la scrittura ha bisogno di validazione, regole di business e transazioni atomiche. La lettura ha bisogno di query ottimizzate, join complessi, aggregazioni e cache. Un singolo modello che fa entrambe le cose diventa un compromesso permanente.

CQRS (Command Query Responsibility Segregation) propone di separare i due percorsi: un modello per la scrittura (Commands) e un modello per la lettura (Queries). Ognuno ottimizzato per il proprio scopo, senza compromessi.

Command e Query: le due meta

Commands: modificare lo stato

Un Command rappresenta un'intenzione di cambiare lo stato del sistema: CreateOrderCommand, CancelOrderCommand, UpdateAddressCommand. Il Command contiene i dati necessari per l'operazione. Un Command Handler riceve il Command, esegue la validazione di business, modifica lo stato e persiste. I Command non restituiscono dati (al massimo un ID o un acknowledge).

Queries: leggere lo stato

Una Query rappresenta una richiesta di dati: GetOrderDetailsQuery, ListPendingOrdersQuery, GetMonthlySalesReportQuery. Un Query Handler riceve la Query e restituisce dati. Le Query non modificano mai lo stato — sono pure letture.

Perché separare: vantaggi concreti

  • Ottimizzazione indipendente: il modello di scrittura usa un ORM con validazione e regole. Il modello di lettura usa query SQL dirette, viste materializzate, cache aggressiva. Nessun compromesso.
  • Scalabilità asimmetrica: la maggior parte delle applicazioni legge 10x più di quanto scrive. Con CQRS puoi scalare il read path (cache, repliche) senza toccare il write path.
  • Modelli dedicati: il DTO di lettura per la dashboard ha campi diversi dall'entita di dominio. Con CQRS sono modelli separati, non proiezioni forzate dello stesso oggetto.
  • Testabilita: Command Handler e Query Handler sono unita testabili indipendentemente.

CQRS senza Event Sourcing

Un malinteso comune e che CQRS richieda Event Sourcing. Non e così. CQRS e la separazione dei modelli di lettura e scrittura — può usare lo stesso database con tabelle diverse, o lo stesso database con query diverse. Event Sourcing e un pattern complementare ma opzionale che aggiunge la persistenza degli eventi come fonte di verità.

La forma più semplice di CQRS in PHP: il Command Handler usa Eloquent per validare e salvare. Il Query Handler usa query SQL dirette o viste per leggere. Stesso database, due percorsi.

Esempio pratico in PHP

  • Write side: CreateOrderHandler riceve un CreateOrderCommand, valida il carrello, verifica il stock, calcola il totale, crea l'ordine tramite il Repository, dispara eventi
  • Read side: OrderDashboardQuery esegue una singola query SQL con join e aggregazioni che restituisce i dati esatti per la dashboard — nessun ORM, nessun N+1, nessun over-fetching

Quando usare CQRS

  • Usa CQRS quando le esigenze di lettura e scrittura sono significativamente diverse
  • Usa CQRS quando la lettura e il collo di bottiglia e vuoi ottimizzarla indipendentemente
  • Usa CQRS quando i report e le dashboard richiedono strutture dati diverse dalle entita di dominio
  • Non usare CQRS per CRUD semplici dove lettura e scrittura usano lo stesso modello senza problemi
  • Non usare CQRS se il team non distingue chiaramente Command da Query: la separazione forzata diventa confusione

CQRS non e un'architettura completa: e un principio di separazione. Applicato nei punti giusti — dove lettura e scrittura hanno requisiti diversi — semplifica entrambi i percorsi. Applicato ovunque per dogma, aggiunge complessità senza beneficio.


👉 Leggi l'articolo completo su iadicola.it

Top comments (0)