Event Sourcing + Aggiornamenti a più aggregati

7

Sto cercando di conoscere l'event sourcing per la potenziale applicazione a un sistema di prenotazione che sto costruendo - Penso di aver compreso i concetti chiave, ma ho difficoltà a capire come gestire i casi in cui gli aggiornamenti a più aggregati / flussi sono richiesto.

Nel mio caso ho modellato due aggregati: Eventi, che tracciano il numero di posti disponibili e Prenotazioni per uno o più posti a un singolo evento che registrano dettagli sui partecipanti. Tutto ciò che ho letto indica che una transazione dovrebbe aggiornare solo un singolo aggregato, tuttavia quando si salva una prenotazione devo aggiornare sia l'evento per tenere traccia dei posti rimanenti all'evento, sia la prenotazione per registrare le modifiche ai dettagli del partecipante. Ciò crea il potenziale di incoerenza:

  • Se aggiorno la prenotazione prima dell'evento, devo gestire il caso in cui l'aggiornamento all'evento fallisce perché i posti non sono più disponibili (questo accade anche se aggiungo un passaggio "Riservare i posti per i prossimi 10 minuti" , perché ad un certo punto devo aggiornare i dettagli della prenotazione e confermare i posti).
  • Viceversa se aggiorno l'evento prima devo gestire casi in cui qualcuno è riuscito a modificare contemporaneamente la prenotazione e c'è un conflitto.
  • In entrambi i casi, un errore imprevisto lascerà inconcludenti la prenotazione e l'evento e non sono sicuro di come potrei gestirlo.

Ho provato a esaminare come vengono gestiti problemi simili in altri domini (ad es. trasferimento di denaro tra conti ), ma non sono al 100% chiaro su come queste strategie assicurino "coerenza finale", e quindi non sono sicuro di come applicarlo nel mio caso.

Come devono essere gestiti gli aggiornamenti a più aggregati collegati?

    
posta Justin 03.12.2015 - 01:16
fonte

3 risposte

4

How should updates to multiple linked aggregates be handled?

Gli aggiornamenti a più aggregati dovrebbero essere applicati in transazioni separate. Se ciò si traduce in una situazione inaccettabile per l'azienda, il conflitto dovrebbe essere rilevato e richiamare la politica di risoluzione.

Spesso accade che la prenotazione sia consentita dall'azienda (negli esempi di spedizioni cargo, spesso si nota che esiste una politica deliberata per la prenotazione oltre la capacità).

In questo caso, ciò significherebbe probabilmente modificare le prenotazioni in modo coerente rispetto alla transazione. Il gestore dei processi, elencando gli eventi di prenotazione, ordinerebbe quindi all'evento di aggiornarsi. L'aggregato di eventi accetterebbe l'aumento del numero di posti riservati e licenzierà due eventi, uno che annuncia il suo attuale numero di posti, e un altro che annuncia che la capacità dell'evento è stata superata. Un altro gestore di processi ascolta l'evento con capacità superata e avvia il processo di mitigazione.

D'altra parte, se l'invarianza di business è davvero un assoluto - se l'azienda non può davvero permettere più prenotazioni di quante non ci sia spazio nell'evento - se la coerenza finale non è abbastanza buona, allora devi accettare il Il fatto che tu abbia un'invarianza di business che richiede dati da due entità diverse, e devi trovare un modello che incorpori tali entità nello stesso aggregato.

    
risposta data 11.12.2015 - 06:54
fonte
4

Everything I have read indicates that a transaction should update only a single aggregate...

È corretto. E la soluzione migliore sarebbe seguire quel consiglio e rifattorizzare il tuo aggregato al limite della transazione: usa un aggregato singolo .

Se ci pensi, il numero di posti disponibili è:

remainingSeats = maxSeats - seatsPurchased

Quindi se hai una sequenza di eventi come questa:

{
    version: 1,        
    type : "EventCreated",
    maxSeats : 500
}

{
    version: 2,
    type : "BookingCreated",
    seats : 4,
}

L'aggregato può essere ricostruito in un oggetto che assomiglia a questo:

{
    version : 2,
    remainingSeats : 496,
}

Significato, non è necessario memorizzare il numero di posti rimanenti. Invece, è possibile ricavare il valore dalle prenotazioni. Questo ti darebbe il singolo aggregato con una singola transazione.

Per tentare di creare un'altra prenotazione, puoi creare un nuovo evento usando il numero di versione successivo, che sarebbe 3 poiché la versione corrente è 2:

{
    version: 3,
    type : "BookingCreated",
    seats : 6,
}

Se l'unicità della versione viene applicata dal database, un commit riuscito nel database indica una prenotazione andata a buon fine; il che significa che l'aggregato non è cambiato nel database da quando è stato letto l'ultima volta.

Tuttavia, se un altro evento arriva prima, esso avrebbe la stessa versione (3), causando il fallimento del commit. Sarebbe un'indicazione che il numero di posti è cambiato sotto i tuoi piedi.

Tale approccio eviterebbe l'incoerenza dei dati. Ma hai ancora bisogno di gestire l'errore. Ad esempio, puoi ricaricare l'aggregato, controllare se l'evento / comando è ancora applicabile e, in tal caso, riprovare (fino a un certo numero di volte).

    
risposta data 29.12.2016 - 07:37
fonte
0

Un'altra soluzione potrebbe essere che l'entità che esegue gli eventi sia un singolo thread, quindi se si dispone di più voli, si potrebbe avere uno di questi thread singoli per ogni volo.

Volo A - Eventi di gestione thread singoli per esso, un singolo evento aggiornerà quindi entrambi gli aggregati in modo che un aggiornamento non si sovrapponga a un altro aggiornamento.

    
risposta data 08.04.2018 - 20:25
fonte

Leggi altre domande sui tag