Un dettaglio importante nella comprensione degli account basati sulle transazioni: l'attributo balance
di account
è in realtà un'istanza di denormalizzazione. È lì per comodità. In realtà il saldo di un account è la somma delle sue transazioni, e non hai bisogno dell'account stesso per avere un saldo.
Tenendo presente questo, l'atto di trasferire un denaro non dovrebbe essere quello di aggiornare account
ma di inserire in 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 comprendo il concetto DDD di aggregati, il seguito sembra pertinente :
The aggregate is a logical boundary for things that can change in a business transaction of a given context. An aggregate can be represented by a single class or by a multitude of classes. If more than one class constitutes to an aggregate then one of them is the so called root class or entity. All access to the aggregate from outside has to happen through the root class.
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 radice è collegato a due elenchi di transazioni (uno per ciascun account); e ogni elenco di transazioni è collegato a un account.
-
Tutti gli accessi al trasferimento dovrebbero essere meditati dall'oggetto root (il transfer
).
Se stai cercando di implementare il supporto per il trasferimento asincrono, il tuo codice principale dovrebbe semplicemente preoccuparsi di creare il trasferimento, in uno stato "in sospeso". Potresti avere un'altra discussione o un lavoro che sposta effettivamente il denaro (inserendolo nella cronologia delle transazioni e quindi aggiornando i saldi) e imposta il trasferimento su "pubblicato".
Se stai cercando di implementare una transazione di trasferimento in tempo reale e bloccante, la logica di business 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 del business dovrebbe essere quello di inserire la transazione di debito nella lista delle transazioni per l'account sorgente (aggiornando il saldo, ovviamente). Questo dovrebbe essere eseguito atomicamente a livello di database (tramite una stored procedure). Dopo che l'addebito si è verificato, il resto del trasferimento dovrebbe essere in grado di avere successo a prescindere dai problemi di concorrenza, in quanto non dovrebbero esserci regole aziendali che impediscano il credito all'account di destinazione.
(Nel mondo reale, i conti bancari hanno il concetto di un memo post che supporta un concetto di pigro due -phase commit La creazione del memo post è leggera e facile, e può anche essere ripristinata senza problemi Conversione del post memo su un post difficile è quando il denaro si muove effettivamente-- questo non può essere ripristinato-- e rappresenta la seconda fase del commit a due fasi, che si verifica solo dopo aver verificato tutte le regole di convalida).