2014-05-04 12 views
10

Sto leggendo Accounting Pattern e sono piuttosto curioso di implementarlo in CQRS.Come modellare il bonifico bancario in CQRS

Penso AccountingTransaction è una radice aggregata in quanto protegge l'invariante:

Nessuna perdita di denaro, che dovrebbe essere il trasferimento da un conto ad un altro.

public class AccountingTransaction { 
    private String sequence; 
    private AccountId from; 
    private AccountId to; 
    private MonetaryAmount quantity; 
    private DateTime whenCharged; 

    public AccountingTransaction(...) { 
     raise(new AccountingEntryBookedEvent(sequence, from, quantity.negate(),...); 
     raise(new AccountingEntryBookedEvent(sequence, to, quantity,...); 
    } 
} 

Quando l'AccountingTransaction viene aggiunto al suo repository. Pubblica diversi AccountingEntryBookedEvent che vengono utilizzati per aggiornare il saldo degli account corrispondenti sul lato query.

Una radice aggregata aggiornata per transazione db, consistenza finale, finora così buona.

Ma se alcuni account applicano vincoli di trasferimento, come ad esempio non è possibile trasferire quantità più del saldo corrente? Posso utilizzare il lato query per ottenere il saldo dell'account, ma sono preoccupato che i dati dal lato query siano obsoleti.

public class TransferApplication { 
    public void transfer(...) { 
     AccountReadModel from = accountQuery.findBy(fromId); 
     AccountReadModel to = accountQuery.findBy(toId); 
     if (from.balance() > quantity) { 
      //create txn 
     } 
    } 
} 

Devo modellare l'account dal lato comandi? Devo aggiornare almeno tre root aggregati per transazione db (da/a account e account txn).

public class TransferApplication { 
    public void transfer(...) { 
     Account from = accountRepository.findBy(fromId); 
     Account to = accountRepository.findBy(toId); 
     Transaction txn = new Transaction(from, to, quantity); 
     //unit or work locks and updates all three aggregates 
    } 
} 

public class AccountingTransaction { 
    public AccountingTransaction(...) { 
     if (from.permit(quantity) { 
      from.debit(quantity); 
      to.credit(quantity); 
      raise(new TransactionCreatedEvent(sequence, from, to, quantity,...); 
     } 
    } 
} 
+0

Vuoi un'applicazione con bank utilizzando DDD? – Developer

+0

@Singh grazie per il tuo commento. Sì, DDD con CQRS. – Hippoom

+0

Ho un progetto su Bankapplication. Lo vuoi? – Developer

risposta

3

Esistono alcuni casi di utilizzo che non consentono un'eventuale coerenza. CQRS va bene ma i dati possono essere devono essere coerenti al 100%. CQRS non implica/richiede coerenza finale.

Tuttavia, lo store del modello di transazione/dominio sarà coerente e il saldo sarà coerente in che memorizza come rappresenta lo stato corrente. In questo caso la transazione dovrebbe fallire comunque, indipendentemente da un lato di query inconsistente. Sarà un'esperienza piuttosto strana, quindi un approccio coerente al 100% potrebbe essere migliore.

3

Ricordo bit di questo, tuttavia M Fowler usa un significato diverso di evento rispetto a un evento di dominio. Usa il termine 'sbagliato', in quanto possiamo riconoscere un comando nella sua definizione 'evento'. Quindi, in pratica, sta parlando di comandi, mentre un evento di dominio è qualcosa che è successo e che non può mai cambiare.

È possibile che non abbia compreso appieno a cui Fowler si riferiva, ma modellerei le cose in modo diverso, più precisamente il più vicino possibile al dominio. Non possiamo semplicemente estrarre un modello che può sempre essere applicato a qualsiasi app finanziaria, i dettagli minori possono cambiare il significato di un concetto.

Nell'esempio di OP, direi che possiamo avere una "transazione" non esplicita: abbiamo bisogno di un conto addebitato con un importo e un altro credito con lo stesso importo. Il modo più semplice, secondo me, è di implementarlo attraverso una saga.

Debit_Account_A -> Account_A_Debited -> Credit_Account_B-> Account_B_Credited = transazione completata.

Questo dovrebbe accadere in pochi ms al massimo secondi e questo sarebbe sufficiente per aggiornare un modello di lettura. Umani e browser sono più lenti di pochi secondi. E un utente sa di colpire F5 o di aspettare qualche minuto/ora. Non mi preoccuperò molto della precisione del modello letto.

Se la transazione è esplicita, il dominio ha una nozione di transazione e l'azienda memorizza realmente le transazioni che è una storia completamente diversa.Ma anche in questo caso, probabilmente la Transazione verrebbe definita da un numero di account id e da alcuni importi e forse da una flag completata. Tuttavia, a questo punto è inutile continuare, perché lo in realtà dipende dalla definizione del dominio e dai casi d'uso.

0

Solo due parole: "Event Sourcing" con il modello di prenotazione. E forse, ma non sempre, potrebbe anche essere necessario il modello "Sagas".