Gestione della saga nel MarketplaceController
Nel controller del Marketplace, le operazioni di acquisto (buyItem) e vendita (sellItem) coordinano due sistemi distinti:
il servizio AvatarClient (sistema esterno) e il MarketplaceService (dominio locale).
Saga di acquisto (buyItem)
L'operazione si articola in tre passi in sequenza, con due flag booleani — moneySpent e inventoryAdded — che tracciano lo stato di avanzamento per poter costruire il rollback esatto.
- Passo 0 — pre-condizione: prima di avviare la saga, viene verificato che l'avatar abbia il livello sufficiente tramite canBuyItem(). Si tratta di un guard sincrono: se fallisce, si risponde immediatamente con 403 Forbidden senza toccare nessun sistema.
- Passo 1 — spendMoney: viene chiamato avatarClient.spendMoney(). Se ha successo, il flag moneySpent viene impostato a true.
- Passo 2 — addItemToInventory: viene aggiunto l'oggetto all'inventario dell'avatar. Se ha successo, inventoryAdded diventa true.
- Passo 3 — buyItem: solo dopo aver completato entrambe le operazioni remote, il marketplace viene aggiornato localmente tramite marketplaceService.buyItem().
Compensazione (rollback): se un qualsiasi passo lancia RestClientException o AvatarCommunicationException,
il blocco catch esegue il rollback selettivo usando i flag:
- se inventoryAdded è true : chiama removeItemFromInventory
- se moneySpent è true : chiama earnMoney
La compensazione stessa può fallire (catch annidato):
in quel caso viene sollevata un'eccezione con un messaggio che segnala l'inconsistenza residua, e il chiamante riceve 502 Bad Gateway.
Saga di vendita (sellItem)
Stessa struttura, passi invertiti.
- Passo 1 — removeItemFromInventory: l'oggetto viene rimosso dall'inventario dell'avatar. Se ha successo, removedFromInventory diventa true.
- Passo 2 — earnMoney: l'avatar riceve il denaro dalla vendita. Se ha successo, earnedMoney diventa true.
- Passo 3 — sellItem: il marketplace viene aggiornato localmente tramite marketplaceService.sellItem().
Compensazione (rollback): se un qualsiasi passo lancia RestClientException o AvatarCommunicationException,il blocco catch esegue il rollback selettivo usando i flag:
- se earnedMoney è true → chiama spendMoney
- se removedFromInventory è true → chiama addItemToInventory
Garanzie e limiti della soluzione
| Proprietà | Valore |
|---|---|
| Tipo di saga | Choreography-based con orchestrazione inline nel controller |
| Consistenza garantita | Eventual consistency — non atomicità forte |
| Idempotenza | Non garantita |
| Durabilità | Nessuna — crash durante la compensazione lascia lo stato inconsistente |
| Logging | Ogni passo è tracciato via MarketplaceLogger |
Il punto debole principale è l'assenza di persistenza della saga: se il server crasha tra un passo e il successivo, nessun meccanismo automatico riprende la compensazione. In un sistema di produzione si considererebbe l'uso di un saga log persistente, oppure si delegherebbe l'orchestrazione a un message broker con garanzie di consegna.