Architettura Event-Driven in Pimcore: Implementare un Sistema di Workflow Asincrono con Symfony Messenger
Chiunque abbia gestito un’istanza Pimcore in produzione con centinaia di migliaia di DataObjects e Assets sa cosa significa vedere l’interfaccia admin bloccarsi durante un’importazione massiva. O peggio, ricevere una chiamata alle 3 di notte perchΓ© il cron di sincronizzazione con l’ERP ha saturato la memoria del server.
La soluzione non Γ¨ “comprare piΓΉ RAM”. La soluzione Γ¨ ripensare l’architettura: spostare le operazioni pesanti fuori dal ciclo request-response e orchestrarle in modo asincrono. Pimcore, essendo costruito su Symfony, ci offre Messenger come strumento nativo per farlo. Configurarlo correttamente per un contesto PIM/DAM enterprise richiede perΓ² una comprensione profonda sia di Messenger che delle peculiaritΓ di Pimcore.
In questo articolo costruiremo un sistema di workflow asincrono completo, partendo dalla configurazione dei transport fino all’implementazione del Saga Pattern per operazioni distribuite. Vedremo come gestire importazioni di 500.000 prodotti senza impattare gli utenti, come orchestrare la generazione di thumbnail su piΓΉ worker e come implementare retry intelligenti che non trasformino un problema temporaneo in un disastro.
Prerequisiti
- Pimcore 11.x con PHP 8.2+
- Conoscenza solida di Symfony (service container, eventi, console commands)
- Redis 7+ o RabbitMQ 3.12+ installato e configurato
- FamiliaritΓ con i concetti di message queue (producer, consumer, acknowledgment)
- Docker per l’ambiente di sviluppo (opzionale ma consigliato)
Assumo che tu abbia giΓ un’installazione Pimcore funzionante. Se stai partendo da zero, la documentazione ufficiale copre l’installazione base.
Architettura e Concetti Chiave
Prima di scrivere codice, capiamo cosa stiamo costruendo:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PIMCORE APPLICATION β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β ββββββββββββββββ ββββββββββββββββββββ ββββββββββββββββββββββββ β
β β HTTP β β Event β β Message β β
β β Request ββββββΆβ Dispatcher ββββββΆβ Bus β β
β ββββββββββββββββ ββββββββββββββββββββ ββββββββββββ¬ββββββββββββ β
β β β
β ββββββββββββββββ ββββββββββββββββββββ β β
β β Admin UI ββββββΆβ Pimcore Events ββββββββββββββββββ€ β
β ββββββββββββββββ β (postUpdate, β β β
β β postAdd, etc) β β β
β ββββββββββββββββ ββββββββββββββββββββ β β
β β CLI/Cron ββοΏ½οΏ½ββββββββββββββββββββββββββββββββββββββββ β
β ββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MESSAGE TRANSPORTS β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββοΏ½οΏ½οΏ½βββ βββββββββββββββββββ βββββββββββββββββββββββββββββββ β
β β Redis Stream β β RabbitMQ β β Doctrine (fallback) β β
β β ββββββββββββββββ β βββββββββββββββ β β βββββββββββββββββββββββββ β β
β β β’ async_high β β β’ async_bulk β β β’ async_low β β
β β β’ asset_proc β β β’ sync_external β β (per ambienti senza Redis) β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββοΏ½οΏ½οΏ½ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β WORKER POOL β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββββββββββ β
β β Worker 1 β β Worker 2 β β Worker 3 β β Worker N β β
β β (high pri) β β (high pri) β β (bulk) β β (asset proc) β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββββββββββ β
β β
β ββββββββββββββββββββββββ β
β β Dead Letter Queue β β
β β + Monitoring β β
β ββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
I concetti fondamentali:
- Message: un oggetto serializzabile che rappresenta un’operazione da eseguire (es.
ProcessProductImportMessage) - Handler: la classe che esegue l’operazione quando il messaggio viene consumato
- Transport: il sistema di storage/delivery dei messaggi (Redis, RabbitMQ, Doctrine)
- Worker: il processo che consuma i messaggi da un transport e li passa agli handler
- Envelope: il wrapper che contiene il messaggio piΓΉ i metadata (stamps)
Implementazione Passo-Passo
Configurazione Multi-Transport per Scenari Pimcore
La configurazione di default di Messenger non Γ¨ adatta a un PIM enterprise. Dobbiamo definire transport separati per tipologie di operazioni diverse, con caratteristiche di performance e resilienza specifiche.
| |
β οΈ Attenzione: non usare mai
sync://in produzione per operazioni pesanti. Γ utile solo per test e debugging.
π‘ Suggerimento: il
claim_intervaldi Redis Streams Γ¨ fondamentale. Se un worker crasha, i messaggi “pending” vengono reclamati da altri worker dopo questo intervallo. Impostalo in base al tempo massimo previsto per l’elaborazione di un messaggio.
Configurazione delle variabili d’ambiente:
| |
Event Subscribers per Intercettare Operazioni CRUD
Il cuore di un sistema event-driven in Pimcore sono gli Event Subscribers che reagiscono alle operazioni su DataObjects e Assets. L’obiettivo Γ¨ intercettare le modifiche e dispatchare messaggi invece di eseguire operazioni sincrone.
| |
π Nota: il flag
$processingBatchΓ¨ statico intenzionalmente. Durante importazioni massive, vogliamo prevenire la generazione di migliaia di messaggi individuali. Alla fine del batch, genereremo un singolo messaggio di “reconciliation”.
Ecco un esempio di messaggio ben strutturato:
| |
Saga Pattern per Workflow Complessi
Le operazioni reali in un PIM/DAM non sono mai atomiche. Un’importazione prodotto tipica coinvolge: creazione DataObject, upload asset, generazione thumbnail, sincronizzazione multi-canale, indicizzazione search. Se uno step fallisce a metΓ , dobbiamo gestire il rollback.
Il Saga Pattern ci permette di orchestrare queste operazioni come una sequenza di step con compensating transactions.
| |