Come utilizzare le funzionalità specifiche del database, come le funzionalità di incremento / decremento, in DDD?

3

Ho un'entità che ha una proprietà balance in dollari. Ogni volta che si verifica un evento che fa cambiare il saldo, come un debito, all'interno della stessa transazione, il balance deve essere decrementato dell'importo del debito (come una sorta di valore "di riepilogo", in modo che una query DB abbia vinto " t devono essere eseguiti per recuperare il bilancia ogni volta.)

Ci sono due modi per farlo:

  1. Posso utilizzare la funzionalità lock for update nel DB per impedire letture dal valore balance fino al termine dell'elaborazione. In questo modo, posso modificare il saldo nella mia entità, ad esempio $user->lowerBalance(1000) , e quindi eseguire il commit del valore in un repository. Lo svantaggio è che questa può essere un'applicazione ad alto carico e non voglio impedire a nessuno di leggere il valore.
  2. L'altra opzione è utilizzare la funzione di decremento del database: update users set balance = balance - 1000 . Ciò garantirà che, anche se due aggiornamenti vengono effettuati quasi alla stessa ora, il valore sarà sempre corretto. Il valore effettivo non verrà impostato o conosciuto nel livello del dominio finché non viene interrogato.

Voglio usare l'opzione 2, ma come faccio a modellarla? Un modo è quello di chiamare il metodo del repository all'interno del metodo lowerBalance() nella classe Entity, ma poi ho accoppiato l'entità dominio al livello infrastruttura. Inoltre, se voglio l'ultimo valore, dovrei assicurarmi che il repository recuperi l'entità. Forse è ok in questo caso, poiché il libro blu dice che i livelli possono dipendere dagli strati sottostanti.

Qualche suggerimento su come può essere strutturato?

    
posta Blossoming_Flower 07.06.2018 - 02:32
fonte

3 risposte

1

La tua intera applicazione dovrebbe consistere in cose che sono rilevanti per il business, non solo un insieme limitato di "oggetti dominio". Questa intuizione potrebbe effettivamente entrare in conflitto con il libro blu, che supporta un'architettura a strati e l'esistenza di oggetti tecnici.

Quello che sto dicendo è che alcune funzioni aziendali potrebbero essere implementate come funzione esterna del database. Questo è naturale per i progetti che non modellano i dati , ma la funzione .

Un design che ho fatto in Java qualche tempo fa:

public interface Customer {
    void freezeCreditCards();
}

Questa è un'interfaccia che è rilevante per il business. Il freezeCreditCards() è direttamente dal documento dei requisiti. Ora, invece di modellare ulteriormente i dati, come le carte di credito, i numeri delle carte, questa era l'implementazione:

public final class DatabaseCustomer {
    private final long customerId;

    public DatabaseCustomer(long customerId) {
        this.customerId = customerId;
    }

    @Override
    public void freezeCreditCards() {
        sql("update creditcard set frozen = 1 where customerid = :customerid", customerid);
    }
}

Il DatabaseCustomer non aveva "dati" sulle carte, non ce n'era bisogno. Certo, ora sa del database. Ma questa applicazione cambierà mai il database in qualcosa di diverso? Forse, ma certamente non spesso.

Ecco una mia presentazione su questa linea di pensiero, chiamata Design orientato al dominio orientato agli oggetti .

Riepilogo : puoi mantenere il saldo nel database, niente di male in questo. Crea un'astrazione appropriata (oggetto) con metodi pertinenti alla tua attività per manipolarla, e stai bene.

    
risposta data 07.06.2018 - 08:50
fonte
0

In qualsiasi DBMS moderno, non è praticamente necessario utilizzare direttamente i costrutti di blocco.

Dì tu

Whenever an event occurs that causes the balance to change ... within the same transaction

Intendi una transazione commerciale o, più utile, una base di dati?

Quindi, se stavi trasferendo fondi (o qualsiasi altra cosa) tra due utenti diversi, dovresti fare qualcosa del genere:

begin transaction ; 

update users set balance = balance - :value where user_id = :user1 ; 
update users set balance = balance + :value where user_id = :user2 ; 

-- other updates that might be required 

commit ; 

Questi sono garantiti dal DBMS per accadere come una singola unità di lavoro, tutto o niente.

    
risposta data 07.06.2018 - 12:33
fonte
0

Id consiglio di aggiungere una riga per ogni transazione e calcolare il saldo al volo in quanto ha un numero se i vantaggi.

La più importante è la capacità di riprodurre la bilancia in modo accurato. Puoi vedere da dove viene l'equilibrio. Questo è importante dal punto di vista della revisione e della prevenzione delle frodi.

Anche le transazioni con timestamp rendono più facile il calcolo delle vendite mensili a fini contabili.

Consente inoltre di apportare modifiche e inversioni, che possono essere visualizzate su una fattura o una dichiarazione.

Come altri hanno già detto, il db dovrebbe garantire la transazione ... ma gli incidenti accadono ancora. Le sue due possibili transazioni potrebbero essere in competizione per un blocco e 1 transazione diventa una vittima del deadlock. A seconda di quanto bene l'app gestisce gli errori, riproverà? Supponi che abbia avuto successo o muoia? Come faresti a sapere se aveva aggiornato il bilancio o meno prima che fallisse?

Lo progetterei in modo tale che se si riprende da un guasto o da un incidente, puoi essere sicuro di riprenderti.

    
risposta data 11.06.2018 - 12:41
fonte