Il registro degli eventi è la tua unica fonte di verità. Non ci si può fidare dei dati nelle proiezioni, perché alla fine è coerente e non deve essere aggiornato. Come hai detto, puoi effettivamente ottenere l'ultimo stato della tua entità di dominio leggendo tutti gli eventi relativi a quella specifica entità e convertendoli nella tua rappresentazione finale.
Quindi, se un account non può mai essere sovrascritto, è necessario conoscere il suo saldo, è necessario calcolarlo nell'oggetto dominio dal flusso di eventi. Questo non è solo raccomandato ma necessario.
Naturalmente, se le tue proiezioni sono tanto semplici quanto l'ultima (eventualmente coerente) rappresentazione di stato delle tue entità, potrebbe sembrare che tu stia duplicando la logica. Tuttavia, lo stato più recente di un'entità nella maggior parte dei casi non è la sola rappresentazione dell'entità, cioè come vorresti vederli i dati.
Per diversi scopi è possibile (e con grandi sistemi sono suscettibili di) avere proiezioni diverse. Alcuni possono essere entità, ma altre proiezioni possono essere intere aggregazioni. Con queste aggregazioni potresti quindi utilizzare un evento MoneyWasDeposited
da tutte le entità BankAccount
per aggiornare un flusso di cassa mensile per vedere quanti soldi stanno effettivamente scorrendo nel tuo sistema.
La forza delle proiezioni arriva nel momento in cui scopri che hai bisogno di diverse rappresentazioni di - internamente - gli stessi dati. Grazie alla pre-creazione della proiezione, l'aumento della complessità della vista non rallenta i tempi di caricamento per gli utenti finali.
Per dare seguito alla risposta di VoiceOfUnreason, mentre ad es. condividere la logica per il calcolo dell'equilibrio ha senso in termini di principio di ESSICCAZIONE (in modo che non ti ripeta), quando in realtà praticando ES, ho imparato che per lo più non lo fa.
Un evento potrebbe essere simile a questo:
class MoneyWasDeposited: IEvent
{
public BankAccountId ToBankAccountId;
public Money Amount;
}
Un aggregato generato da un evento di solito elaborava questo evento utilizzando un meccanismo simile a questo:
class BankAccount
{
private BankAccountId id;
private Money currentBalance;
public void ReplayFromEvents(List<IEvent> events)
{
for (var event in Events)
{
ApplyEvent(event);
}
}
private void ApplyEvent(IEvent event)
{
if (event is MoneyWasDeposited)
{
currentBalance = currentBalance.Add(event.Amount);
}
}
}
mentre è probabile che la proiezione sia simile a questa:
class BankAccountProjection
{
private MySqlConnection database;
public void HandleMoneyWasDeposited(MoneyWasDeposited event)
{
var statement = database.Prepare("
UPDATE
bank_accounts
SET balance = balance + :depositedAmount
WHERE id = :id
");
statement.BindValue("depositedAmount", event.Amount.Value);
statement.BindValue("id", event.ToBankAccountId.ToString());
statement.ExecuteNoResult();
}
}
Quindi, mentre il tuo aggregato calcola il valore in codice dall'evento, la proiezione di solito applica la modifica direttamente al tuo negozio di read-model e lascia che il motore di archiviazione si occupi della modifica dei dati.
Come puoi vedere, non c'è davvero alcun modo per ridurre la duplicazione con questo, perché l'aggregazione e la proiezione utilizzano meccanismi molto diversi per ottenere il risultato desiderato.
Ora, non sto dicendo che non è possibile estrarre l'intera proiezione dall'archivio del modello di lettura, aggiornare il modello di lettura in memoria e scaricarlo per l'aggiornamento, ma di solito non è il modo in cui è fatto, come aggiunge un inutile SELECT
e ottiene comunque lo stesso risultato.
Potresti chiedere: la logica di calcolo del saldo non è in un singolo posto. Quindi, cosa succede se qualcuno, per errore, rende la logica della proiezione errata?
Considerare lo scenario in cui qualcuno ha scritto un segno meno all'aggiornamento del saldo delle proiezioni quando il denaro è stato depositato. Ciò significherebbe che ogni volta che hai depositato denaro sul tuo account, all'improvviso ne avresti di meno.
Dovresti quindi eseguire le seguenti operazioni:
- cambia il segno meno in un segno più nel metodo di proiezione,
- sposta immediatamente la modifica sulla produzione, in modo che tutte le modifiche da ora in poi siano corrette,
- crea un nuovo indice di proiezione nel tuo negozio di modelli di lettura, ad es.
bank_accounts_fixed
,
- esegui un'altra istanza di gestore di proiezione conto bancario dall'inizio del tuo sistema e inserisci invece i dati in
bank_accounts_fixed
,
- dopo che la proiezione locale è terminata, scambia la configurazione in produzione per utilizzare i dati di
bank_accounts_fixed
al posto di bank_accounts_fixed
,
- elimina l'indice
bank_accounts
originale.
E tutti i tuoi utenti vedranno improvvisamente saldi corretti. Puoi farlo perché sai che i dati nel tuo archivio eventi sono garantiti per essere corretti (dovrebbe essere).