DDD, Saga e Event-sourcing: un'azione di compensazione può essere semplicemente un'eliminazione nell'archivio eventi?


15

Mi rendo conto che la domanda di cui sopra probabilmente solleva alcuni "what ??", ma lasciami provare a spiegare:

Sto cercando di avvolgere la mia testa su un paio di concetti correlati, fondamentalmente il modello Saga ( http://www.rgoarchitects.com/Files/SOAPatterns/Saga.pdf ) in combinazione con Event-sourcing (un concetto DDD : http://en.wikipedia.org/wiki/Domain-driven_design )

Un buon post che lo mette insieme: https://blog.jonathanoliver.com/cqrs-sagas-with-event-sourcing-part-ii-of-ii/

Sto arrivando alla domanda tra un minuto, ma penso che dovrei provare a riassumere prima quello che ho capito (che potrebbe benissimo essere sbagliato, quindi per favore correggi se è così) poiché questo potrebbe influenzare il motivo per cui sono ponendo la domanda per cominciare:

  1. Il modello Saga è una sorta di broker che ha dato un'azione (utente finale, automatizzato, ecc. Essenzialmente qualsiasi cosa che cambierà i dati) divide quell'azione in attività aziendali e invia ciascuna di queste attività come messaggi a un bus dei messaggi che a sua volta lo invia alle rispettive radici aggregate di cui occuparsi.
  2. Queste radici aggregate possono operare in modo completamente autonomo (bella separazione delle preoccupazioni, grande scalabilità, ecc.)
  3. Una stessa istanza Saga non contiene alcuna logica aziendale, che è contenuta nelle radici aggregate a cui invia attività. L'unica "logica" contenuta nella Saga è la logica "di processo", (spesso implementata come una macchina a stati), che determina in base alle azioni ricevute (così come agli eventi di follow-up) cosa fare (ovvero: quali attività inviare)
  4. I modelli Saga implementano una sorta di modello di transazione distribuita. Vale a dire: quando una delle radici aggregate (che funzionano di nuovo in modo autonomo, senza sapere dell'esistenza di ciascuno) non riesce, l'intera azione potrebbe dover essere annullata.
  5. Questo viene implementato avendo tutte le radici aggregate, al completamento della loro relazione di attività alla Saga. (In caso di successo e di errore)
  6. Nel caso in cui tutte le radici aggregate restituiscano un successo, la macchina da stato interna se la Saga determina cosa fare dopo (o decide che è fatto)
  7. In caso di fallimento, la Saga rilascia a tutte le radici aggregate che hanno preso parte all'ultima azione una cosiddetta Azione di compensazione, ovvero: un'azione per annullare l'ultima azione che ciascuna delle radici aggregate ha fatto.
  8. Questo potrebbe semplicemente fare un "Meno 1 voto" se l'azione fosse "più 1 voto", ma potrebbe essere più complicato come ripristinare un blogpost alla sua versione precedente.
  9. Event-sourcing (vedi il blogpost che combina i due) mira a esternalizzare il salvataggio dei risultati di ciascuna delle attività che ciascuna delle radici aggregate intraprende in un negozio di eventi centralizzato (i cambiamenti sono chiamati 'eventi' in questo contesto)
  10. Questo archivio eventi è la "versione singola della verità" e può essere utilizzato per riprodurre lo stato di tutte le entità semplicemente ripetendo gli eventi memorizzati (essenzialmente come un registro eventi)
  11. Combinando i due (ovvero: lasciando che le radici aggregate utilizzino l'Evento-sourcing per esternalizzare il salvataggio delle loro modifiche prima di riferire alla Saga) si creano molte belle possibilità, una delle quali riguarda la mia domanda ...

Sentivo di aver bisogno di togliermelo dalla spalla, dato che è molto da capire in una volta sola. Dato questo contesto / mentalità (di nuovo, si prega di correggere se sbagliato)

la domanda: quando una radice aggregata riceve un'azione compensata e se quella radice aggregata ha esternalizzato i suoi cambiamenti di stato usando il sourcing degli eventi, l'azione compensata in tutte le situazioni non sarebbe solo una cancellazione dell'ultimo evento nell'archivio eventi per quello dato radice aggregata? (Supponendo che l'implementazione persistente consenta l'eliminazione)

Ciò avrebbe molto senso per me (e sarebbe un altro grande vantaggio di questa combinazione) ma, come ho detto, potrei fare queste ipotesi basate su una comprensione errata / incompleta di questi concetti.

Spero che questo non sia stato troppo prolisso.

Grazie.

Risposte:


9

Non dovresti eliminare gli eventi dal tuo negozio di eventi. Questi rappresentano qualcosa che è successo. Se succede qualcosa adesso, allora dovresti saperlo anche tu, quindi aggiungi un evento che costringerà il tuo aggregato a tornare a uno stato precedente. È un peccato eliminare le informazioni dal tuo database.

pensa all'esempio del carrello di Greg Young e degli elementi rimossi. Potrebbe essere domani che l'informazione dell'azione compensativa possa avere qualsiasi valore.

Per quanto riguarda la saga, ogni cosa è corretta, per quanto io conosca me stesso. Dovresti pensare alla saga come a una macchina statale. Fa solo quello.

È un comando che la saga sta emettendo per dire all'aggregato di fare qualcosa. Di questa azione ci sarà un evento. Questo evento potrebbe essere l'azione correttiva di cui hai bisogno o potrebbe essere necessario denormalizzare in una vista in modo che alcuni utenti lo vedano per decidere cosa fare.

Dipende dalle regole della tua azienda. O c'è una soluzione semplice: la radice aggregata sa che in tal caso, lo fai, o la scelta è troppo complessa per essere eseguita programmaticamente (i costi di sviluppo sono troppo alti) e quindi, potresti proporlo a qualche utente, che potrebbe scegliere tra diverse azioni. Tutto dipende dal contesto dell'azione compensativa. Questa è pura regola aziendale. Cosa dovremmo fare in questo caso o in quel caso. Questo è qualcosa da chiedere al tuo esperto di dominio e quindi scegliere con lui, se è meglio sviluppare una soluzione automatica o se la soluzione è chiedere all'utente Ronald R., perché è lui a rispondere di solito a questa domanda.


Come farebbe l'aggregato a sapere a quale stato ripristinare? Sarebbe permesso eseguire query sul negozio di eventi o qualcosa del genere?
Geert-Jan

Non dovrebbe tornare ma aggiungere un altro evento. È un comando che la saga sta emettendo per dire all'aggregato di fare qualcosa. Di questa azione ci sarà un evento.
Arthis,

Si Capisco. "Revert" è stato probabilmente scelto male. Considera quanto segue: Potrei usare Event Sourcing con Sagas per modellare, tra l'altro, i flussi di approvazione / pubblicazione dei documenti. Che cosa succede se un editor ha inviato un'azione ChangeBlogBody che alla fine è persistita come evento BlogBodyChanged nell'archivio eventi. Successivamente, per qualche motivo, viene emessa un'azione di compensazione. In che modo l'aggregato potrebbe sapere quale evento compensativo generare che si traduce nei contenuti del Blog Body come era appena prima dell'emissione dell'azione ChangeBlogBody?
Geert-Jan

Dipende dalle regole della tua azienda. O c'è una soluzione semplice: la radice aggregata sa che in tal caso, lo fai, o la scelta è troppo complessa per essere eseguita a livello di codice (i costi di sviluppo sono troppo alti) e quindi, potresti proporlo a qualche utente, che potrebbe scegliere tra diverse azioni.
Arthis,

Tutto dipende dal contesto dell'azione compensativa. Questa è pura regola aziendale. Cosa dovremmo fare in questo caso o in quel caso. Questo è qualcosa da chiedere al tuo esperto di dominio e quindi scegliere con lui, se è meglio sviluppare una soluzione automatica o se la soluzione è chiedere all'utente Ronald R., perché è lui a rispondere a questa domanda di solito.
Arthis,

6

Per completezza ho pensato di includere un frammento pertinente di Martin Fowler sui modi per ripristinare lo stato:

Oltre agli eventi che si svolgono in avanti, è anche utile per loro essere in grado di invertire se stessi.

L'inversione è la più semplice quando l'evento viene lanciato sotto forma di differenza. Un esempio di questo sarebbe "aggiungi $ 10 al conto di Martin" invece di "imposta il conto di Martin a $ 110". Nel primo caso posso invertire semplicemente sottraendo $ 10, ma nel secondo caso non ho abbastanza informazioni per ricreare il valore passato dell'account.

Se gli eventi di input non seguono l'approccio della differenza, l'evento dovrebbe assicurarsi che memorizzi tutto il necessario per l'inversione durante l'elaborazione. È possibile farlo memorizzando i valori precedenti su qualsiasi valore modificato o calcolando e memorizzando le differenze sull'evento.

Da: http://martinfowler.com/eaaDev/EventSourcing.html


1

concettualmente:

  • L'evento è qualcosa che è successo. Il passato non può essere cambiato.
  • Il comando è qualcosa da fare. Il comando potrebbe non avvenire (potrebbe essere rifiutato).

Possiamo cambiare il nostro stato futuro solo eseguendo un altro comando (Compensate Action) che dovrebbe comportare che gli eventi cambino lo stato dell'applicazione.

Per "confondere" la domanda, pensa a questa frase "elimina l'ultimo evento":

  • Cosa succede se l'ultimo evento non è quello che deve essere eliminato? Cosa succede se si verificano altri eventi dopo quello da eliminare? Anche questi eventi devono essere cambiati (poiché il loro stato di base sarebbe cambiato cancellando l'evento precedente a loro).
  • E se l'evento fosse stato inviato a un sistema esterno? Potresti non avere accesso per correggerne lo stato se non l'invio di un altro evento.

In breve, non è possibile eliminare eventi all'interno del modello CQRS.

Puoi restringere il tuo Event Store creando uno stato di istantanea (che si basa su tutti gli eventi che portano a quello stato), ma questo aspetto fisico non ha nulla a che fare con il concetto di evento.


0

Geert-Jan, anche io penso che l'azione di compensazione possa semplicemente eliminare gli eventi corrispondenti. Ha senso e mostra un altro vantaggio del modello di progettazione di approvvigionamento di eventi: implementazione più semplice del modello di progettazione di Transazione compensativa.

Alcuni sostengono che la cancellazione dell'evento viola i "principi" di approvvigionamento di eventi o CQRS. Penso che sia una generalizzazione limitante. Penso che sia giusto eliminare un evento se è stato creato nell'ambito di una transazione globale che viene annullata. Considera lo pseudocodice:

try {
    newEvents = processMycommand(myCommand)
    for (Event newEvent : newEvents)
        EventStore.insertEvent(newEvent);
} catch (Exception e) {
    EventStore.rollback();
}

Supponiamo che il tuo archivio eventi sia un database transazionale. Nello pseudocodice, puoi immaginare la situazione in cui hai inserito il primo evento, ma quando si tenta di inserire il secondo evento, viene generata un'eccezione. Il comando di rollback ripristinerebbe naturalmente l'inserimento del primo evento. È ragionevole?

Se è ragionevole per una transazione di database ACID (transazione con commit / rollback), perché non dovrebbe essere ragionevole per una transazione di compensazione?

Quando si esegue la transazione Saga globale, le modifiche ai dati possono essere annullate (mediante compensazione). Non è necessario conservare l'evento creato durante la transazione perché la transazione non è stata completata.

Ora, se la compensazione tenta di eliminare un evento e quell'evento non è l'evento più recente su un oggetto, la cancellazione non dovrebbe avvenire. Ma in generale, è improbabile che ciò accada, soprattutto nelle soluzioni ad alta intensità di lettura.


0

Giochiamo con il pensiero che l'archiviazione degli eventi sia solo dati che vuoi salvare. Ad esempio, possiamo avere un disco rigido, possiamo iniziare dall'inizio e scrivere dati su di esso. Ogni volta che riceviamo un evento, aggiungiamo i nostri dati precedenti, quindi continuiamo a scrivere sul disco dove ci siamo fermati l'ultima volta. Quando vogliamo rimuovere un evento, torniamo indietro, rimuoviamo quella parte dal disco e lasciamo un vuoto. Il ritorno da solo rallenta la memorizzazione degli eventi, ma possiamo conviverci. Se utilizziamo un filesystem, questo cercherà di colmare il gap con gli ultimi eventi, quindi finiremo per avere una memoria frammentata lenta o potremo eseguire la deframmentazione. Nessuno dei due è qualcosa che ci piace. Se stiamo parlando di database, questo si ottiene se si utilizza un database relazionale anziché un database solo accodato per la memorizzazione dei propri eventi:

inserisci qui la descrizione dell'immagine rif: https://cambridge-intelligence.com/bringing-time-series-data-to-life-with-keylines/

Ofc. da un sito normale questo non ha importanza, ma queste soluzioni sono progettate per enormi siti Web come Facebook, Google, ecc ... Quindi la domanda non ha senso, perché come cancelleresti un evento in un database di solo accodamento e come chiamate compensazione qualcosa come costruire una macchina del tempo e tornare indietro nel tempo per cambiare o prevenire un evento ???

Per quanto ne so. l'unico modo per risolverlo è creare un nuovo archivio eventi in cui si escludono gli eventi che non si desidera avere e successivamente rimuovendo il vecchio archivio eventi. Ma questa è una soluzione estrema per rimuovere un singolo evento. Se stiamo parlando di GDPR, l'unica buona soluzione relativa che conosco è l'archiviazione dei dati personali crittografati nell'archivio eventi e la rimozione della chiave di crittografia da un database diverso.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.