Come modellare uno scenario di acquisto utilizzando il sourcing di eventi

2

Sto provando a programmare un semplice scenario di acquisto utilizzando il sourcing di eventi:

---------------     ---------------       ----------------
| OrderService |   | ProductService |    | PaymentService |
----------------   ------------------    -----------------
        |________________|_____________________|
                         |
                --------------------
                |       KAFKA      |
                --------------------
                         |
                --------------------
                |      PROCESSOR   |
                --------------------
                         |
                --------------------
                |     DATABASE     |
                --------------------

Ci sono 3 servizi:

  • Servizio ordini: API che riceve ordini per un prodotto effettuato da un cliente
  • Servizio del prodotto: contiene lo stock di prodotti
  • Servizio di pagamento: effettua il pagamento dell'ordine

Kafka: archivio eventi per i diversi eventi.

Processore: processore CQRS che genera la visualizzazione dello stato corrente.

Database: visualizzazione materializzata dello stato corrente.

Il mio dubbio principale è come gestire un flusso del genere:

  1. Il servizio ordini viene chiamato e crea un evento: OrderPlaced
  2. ProductService verifica la disponibilità di un prodotto e in base a esso crea un evento ProductReserved o OrderCancelledNoStock.
  3. PaymentService ha elaborato il pagamento in caso di evento ProductReserved, producendo un nuovo evento: OrderCompleted.

Poiché si tratta di un puro approccio di sourcing degli eventi, ProductService non ha un database.

  • È una buona pratica considerare la vista materializzata nel DATABASE come la fonte della verità del sistema riguardante lo stock? Secondo me l'unica fonte di verità dovrebbe essere gli eventi, non dovrebbe?
  • Vedo molto probabilmente trovare incongruenze dovute alle latenze nel processo CQRS DATABASE, ecco perché l'utilizzo del database come sorgente di thruth è davvero rischioso. Tuttavia avere una visione materializzata sembra essere l'unico modo per conoscere lo stock attuale dei prodotti e del processo o non un ordine di prodotto.

Casi problematici che mi vengono in mente:

  • Due OrderPlaced simultanei per lo stesso prodotto arrivano a ProductService. Questo chiama il DATABASE per verificare se c'è disponibilità del prodotto (1 prodotto rimasto). Dato che va bene, entrambi creano un evento ProductReserved per lo stesso prodotto e PaymenService lo elaborerà.
  • Un ordine OrderPlaced arriva a ProductService. 1 prodotto rimasto quindi pubblica ProductReserved. Arriva un altro ordine per lo stesso prodotto prima che il DATABASE venga aggiornato dal processo CQRS. Quindi pubblica un altro prodotto conservato nonostante non abbia abbastanza scorte.

Qual è il modo consigliato di gestire uno scenario come questo?

    
posta codependent 11.07.2017 - 13:59
fonte

2 risposte

0

Quello che hai qui è un processo che genera più servizi. In DDD , questi servizi sono Aggregati, il limite di transazioni più grande possibile. Darò una risposta nel contesto del DDD.

Puoi modellare questo con un gestore Saga / Process ( OrderProcess ) che orchestra il processo di ordinazione da order placed a order completed eventi.

ProductService checks the availability of a product and based on it creates a ProductReserved or OrderCancelledNoStock event.

Infatti, ProductService dovrebbe aumentare ProductReserved evento ma non OrderCancelledNoStock evento. Questo servizio non dovrebbe conoscere gli ordini, solo circa product reserving - separazione delle preoccupazioni. Invece dovrebbe lanciare un'eccezione ProductOutOfStock .

Se viene generato l'evento ProductReserved , OrderProcess lo riceverà e invierà un comando a PaymentService per effettuare il pagamento (ciò dipende dalla procedura di pagamento effettiva).

Se viene generata l'eccezione ProductOutOfStock allora OrderProcess la prenderà e invierà il CancelOrder a OrderService .

Two concurrent OrderPlaced for the same product arrive at ProductService. This calls DATABASE to check if there's product availability (1 product left). Since that's ok both create a ProductReserved event for the same product and PaymenService will process it.

OrderService , se implementato come aggregato generato da un evento, non lo consentirebbe lanciando l'eccezione ProductOutOfStock dopo che tutti i prodotti sarebbero stati riservati. La verifica delle scorte viene eseguita contro l'intero flusso di eventi generati in precedenza e non contro una proiezione. In caso di esecuzione di comandi concorrenti, l'ultimo comando che tenta di mantenere gli eventi non riesce a persisterli, quindi il comando viene ritentato ricaricando tutti gli eventi, compresi quelli generati dal comando wining, e poi fallisce con l'eccezione ProductOutOfStock ;

    
risposta data 11.07.2017 - 19:53
fonte
0

Come regola generale, la convalida dei comandi dovrebbe sempre avvenire sullo storage principale, ovvero sul flusso di eventi. Altrimenti, se convalidi contro una proiezione eventualmente coerente, potresti diventare incoerente, come hai notato correttamente.

A volte può essere utile convalidare in due passaggi: prima contro la proiezione, poi contro la memoria principale. Ad esempio, l'interfaccia utente potrebbe interrogare il database per mostrare all'utente un messaggio "non disponibile" nel caso in cui lo stock sia zero. Ma una volta che l'utente ha effettuato l'ordine, il back-end lo convaliderà contro il flusso di eventi.

    
risposta data 11.07.2017 - 14:47
fonte