Introduzione al pattern CQRS

banner

Nel contesto dei sistemi software moderni, la gestione efficiente delle operazioni di lettura e scrittura sui dati è fondamentale per garantire prestazioni ottimali e scalabilità. Uno dei pattern architetturali che affronta questa sfida è il CQRS (Command Query Responsibility Segregation).

Introduzione al pattern CQRS

CQRS sta per Command Query Responsibility Segregation (Separazione delle Responsabilità tra Comandi e Query). Un acronimo non facile da ricordare!

Ha origine dal concetto di Command Query Separation (separazione tra comandi e query) coniato da Bertrand Meyer con l’idea di dividere i metodi di una classe in due categorie: query, che restituiscono dati senza modificare lo stato, e comandi, che modificano lo stato senza restituire dati.

Il CQRS fa suo questo concetto e lo applica al model layer delle applicazioni, con l’idea di separare il model che aggiorna le informazioni da quello che le legge.

Le implementazioni pratiche di CQRS possono variare, ma di solito comandi e query vengono mantenuti come model completamente separati, spesso collegati a database distinti.

Tipicamente, il model dei comandi gestisce le operazioni CUD (e alcune semplici R) sul proprio database principale, mentre il model delle query può essere mappato su un datastore separato, ad esempio una replica read-only, in modo da eseguire query complesse senza compromettere l’efficienza e il corretto funzionamento del database principale.

In questo scenario poi, il model dei comandi pubblica degli eventi per ogni operazione eseguita, che vengono ricevuti dal lato query per mantenere i due database sincronizzati.

CQRS può essere un ottimo strumento quando si lavora con query complesse, in particolare in applicazioni basate su microservizi. Può rappresentare una valida alternativa per bilanciare gli svantaggi della composizione di API (come le join in memoria di grandi insiemi di dati) quando si creano query che attraversano più microservizi e i loro rispettivi database.

Quando è davvero utile CQRS?

CQRS può aiutare a superare le limitazioni derivanti dall’uso di un singolo datastore per tutte le operazioni CRUD, consentendo ad architetti e sviluppatori di scegliere il database più adatto a ogni scenario di query; e, naturalmente, permettendo una migliore separazione delle responsabilità.

D’altra parte, CQRS introduce maggiore complessità a causa della logica aggiuntiva e dei componenti architetturali supplementari; inoltre, la segregazione tra comandi e query aggiunge una certa latenza necessaria per mantenere sincronizzate le due parti.

Esempio pratico di CQRS

È chiaro che non esiste un’architettura “miracolosa” e che ogni pattern comporta dei compromessi. Come ogni altro modello architetturale, CQRS può essere molto utile, ma solo se utilizzato nel contesto appropriato.

Ad esempio, immaginiamo un sistema di gestione degli ordini in cui gli utenti possono creare, aggiornare e visualizzare gli ordini. In questo caso, potremmo avere due modelli distinti:

  1. Model dei Comandi: Responsabile della gestione delle operazioni di creazione e aggiornamento degli ordini. Questo model interagisce con un database principale per eseguire operazioni CUD (Create, Update, Delete).
  2. Model delle Query: Responsabile della gestione delle operazioni di lettura degli ordini. Questo model interagisce con un database separato ottimizzato per le query, consentendo agli utenti di visualizzare gli ordini in modo efficiente.

L’utilizzo di questo pattern consente di ottimizzare le prestazioni delle operazioni di lettura e scrittura, migliorando la scalabilità complessiva del sistema.

In che modo si concretizza? Quando un utente crea o aggiorna un ordine, il model dei comandi esegue l’operazione sul database principale e pubblica un evento che notifica il model delle query della modifica. Il model delle query, a sua volta, aggiorna il suo database separato per riflettere la nuova stato dell’ordine. In altre parole, se dovessimo applicarlo all’interno di un progetto reale in Python, potremmo avere qualcosa del genere:

class OrderCommandModel:
    def __init__(self, command_db):
        self.command_db = command_db

    def create_order(self, order_data):
        # Logica per creare un ordine nel database principale
        order_id = self.command_db.insert(order_data)
        self.publish_event('OrderCreated', order_id, order_data)

    def update_order(self, order_id, order_data):
        # Logica per aggiornare un ordine nel database principale
        self.command_db.update(order_id, order_data)
        self.publish_event('OrderUpdated', order_id, order_data)

    def publish_event(self, event_type, order_id, order_data):
        # Logica per pubblicare eventi (ad esempio su un message broker)
        pass

class OrderQueryModel:
    def __init__(self, query_db):
        self.query_db = query_db
        
    def get_order(self, order_id):
        # Logica per recuperare un ordine dal database di query
        return self.query_db.find(order_id)
    def handle_event(self, event_type, order_id, order_data):
        # Logica per aggiornare il database di query in base agli eventi ricevuti
        if event_type == 'OrderCreated':
            self.query_db.insert(order_id, order_data)
        elif event_type == 'OrderUpdated':
            self.query_db.update(order_id, order_data)

In questo esempio ci siamo astratti/e dall’uso di librerie specifiche per la gestione dei database o dei message broker, ma l’idea è chiara: abbiamo due modelli distinti che gestiscono separatamente le operazioni di comando e query, migliorando così la scalabilità e le prestazioni del sistema complessivo.

Conclusioni

Il pattern CQRS rappresenta un approccio potente per la gestione delle operazioni di lettura e scrittura nei sistemi software moderni. Separando le responsabilità tra comandi e query, CQRS consente di ottimizzare le prestazioni, migliorare la scalabilità e scegliere i database più adatti a ogni scenario. Tuttavia, è importante valutare attentamente i compromessi introdotti da questo pattern, considerando la complessità aggiuntiva e la latenza necessaria per mantenere sincronizzati i modelli. Quando applicato nel contesto giusto, CQRS può essere uno strumento prezioso per la progettazione di sistemi software efficienti e scalabili.

Post correlati

TheRedCode.it - Il mondo #tech a piccoli #bit

Partners

Community, aziende e persone che supportano attivamente il blog

Logo di Codemotion
Logo di GrUSP
Logo di Python Milano
Logo di Schrodinger Hat
Logo di Python Biella Group
Logo di Fuzzy Brains
Logo di Django Girls
Logo di Improove
Logo del libro open source
Logo di NgRome
Logo de La Locanda del Tech
Logo di Tomorrow Devs
Logo di DevDojo

Vuoi diventare #tech content creator? 🖊️

Se vuoi raccontare la tua sul mondo #tech con dei post a tema o vuoi condividere la tua esperienza con la community, sei nel posto giusto! 😉

Manda una mail a collaborazioni[at]theredcode.it con la tua proposta e diventa la prossima penna del blog!

Ma sì, facciamolo!