Un dettaglio importante nella comprensione dei conti basati sulle transazioni: l' balance
attributo di account
è in realtà un'istanza di denormalizzazione. È lì per comodità. In realtà, il saldo di un conto è la somma delle sue transazioni e in realtà non è necessario che il conto stesso abbia un saldo.
Tenendo presente ciò, l'atto di trasferire un denaro non dovrebbe essere quello di aggiornare account
ma di inserirlo transaction
.
Detto questo, c'è un'altra regola importante: l'atto di aggiungere un transaction
dovrebbe essere atomico con un aggiornamento al (campo di equilibrio denormalizzato di) account
.
Ora, se capisco il concetto di aggregati DDD, sembra rilevante quanto segue :
L'aggregato è un limite logico per le cose che possono cambiare in una transazione commerciale di un determinato contesto. Un aggregato può essere rappresentato da una singola classe o da una moltitudine di classi. Se più di una classe costituisce un aggregato, una di esse è la cosiddetta classe o entità radice. Tutti gli accessi all'aggregato dall'esterno devono avvenire attraverso la classe root.
Quindi, in termini di design DDD, suggerirei:
C'è un aggregato per rappresentare il trasferimento
L'aggregato è composto dai seguenti oggetti: il trasferimento (l'oggetto radice); l'oggetto root è collegato a due elenchi di transazioni (uno per ciascun account); e ogni elenco di transazioni è collegato a un account.
Tutti gli accessi al trasferimento devono essere meditati dall'oggetto root (il transfer
).
Se stai cercando di implementare il supporto per il trasferimento asincrono, il tuo codice principale dovrebbe semplicemente preoccuparti di creare il trasferimento, in uno stato "in sospeso". Potresti avere un altro thread o un lavoro che sposta effettivamente i soldi (inserendoli nella cronologia delle transazioni e quindi aggiornando i saldi) e imposta il trasferimento su "registrato".
Se stai cercando di implementare una transazione di trasferimento in tempo reale, bloccando, la logica aziendale dovrebbe creare un transfer
e quell'oggetto coordinerebbe le altre attività in tempo reale.
In termini di prevenzione dei problemi di concorrenza, il primo ordine di attività dovrebbe essere l'inserimento della transazione di addebito nell'elenco delle transazioni per l'account di origine (aggiornamento del saldo, ovviamente). Questo dovrebbe essere eseguito atomicamente a livello di database (tramite una procedura memorizzata). Dopo che si è verificato l'addebito, il resto del trasferimento dovrebbe riuscire a prescindere dai problemi di concorrenza, in quanto non dovrebbero esserci regole commerciali che impediscono un credito sul conto di destinazione.
(Nel mondo reale, i conti bancari hanno il concetto di un memo post che supporta il concetto di un lazy commit in due fasi. La creazione del post dei memo è leggera e facile, e può anche essere ripristinata senza problemi. Conversione del il post di promemoria su un post rigido è quando il denaro si sposta effettivamente (non è possibile eseguire il rollback) e rappresenta la seconda fase del commit in due fasi, che si verifica solo dopo aver verificato tutte le regole di convalida).