Costruttori di argomenti zero e entità sempre valide

7

Recentemente ho letto molto sulle entità di dominio Always Valid. Sono arrivato a credere che per garantire che le entità siano sempre valide ho bisogno di:

1) Rimuovi l'ossessione primitiva e metti le regole di validazione / dominio nei costruttori dell'oggetto valore come spiegato qui: link . 2) Metti le regole di validazione / dominio nel costitatore di entità o setter di proprietà come spiegato qui: link .

Tuttavia, guardo ad alcuni progetti Open Source come questo: link . Da quello che ho capito, l'autore di questo progetto è un sostenitore del modello di dominio sempre valido e tuttavia guardo qui alla classe InventoryItem: link . Ho notato che sono in grado di farlo:

InventoryItem inventoryItem = new InventoryItem();

o questo:

InventoryItem inventoryItem2 = new InventoryItem(Guid.Empty,null);

Nella mia mente ciò significa che l'entità è inizializzata in uno stato non valido. Questo sembra essere il caso in tutti gli altri progetti Open Source che ho esaminato di recente, ad es. questo: link .

Mi rendo conto che esiste una convalida contestuale in questo progetto open source ( link ). Mi rendo anche conto che gli ORM necessitano di un costruttore vuoto predefinito se mappato sul modello di dominio.

Un oggetto dominio è in uno stato valido se è inizializzato con valori predefiniti usando un costruttore di argomentazione zero / inizializzato con valori vuoti / nulli?

    
posta w0051977 09.06.2018 - 18:00
fonte

2 risposte

6

Prima di affrontare i tipi di considerazioni che vanno nella creazione di oggetti, prima affrontiamo la motivazione alla base della tua domanda: la frase "Sempre valide entità di dominio". Questa frase è, nel migliore dei casi, fuorviante e ha poco senso nel contesto di DDD. Questo dovrebbe essere evidente per due ragioni correlate:

Il primo è che sposta implicitamente l'attenzione dal comportamento del sistema, chiedendo invece di considerare la convalida solo in termini di stato. Mentre superficialmente questo può sembrare sensato (ovviamente la convalida è in termini di stato!), È necessario ricordare che il principio fondamentale del DDD è che un sistema è modellato in base al comportamento . La motivazione per questo è semplicemente che il contesto, o il processo aziendale stesso, è spesso una considerazione importante quando si determina se un pezzo di stato è valido o meno. Modellare un sistema in questo modo può notevolmente ridurne la complessità.

Questo ci porta alla seconda ragione, che riguarda le esigenze pratiche che un tale sistema comporterebbe. Per creare un sistema di "Entità di dominio sempre valide", sarebbe necessario modellare ogni singola permutazione di stato in base ai processi aziendali in cui viene utilizzato lo stato. Un semplice esempio può illustrare i limiti di questo:

Regole:

  • Customer deve essere maggiore di 18 per registrarsi
  • Customer deve essere inferiore a 25 per beneficiare dello sconto sulla registrazione
  • Customer deve essere maggiore di 25 per effettuare la prenotazione

La prima cosa che dovresti notare è che tutte queste regole (come quasi tutte le regole) si applicano ad alcuni processi aziendali. Non esistono nel vuoto. Queste regole sarebbero convalidate su customer.Register() e customer.Reserve() . Ciò si traduce in un paradigma molto più descrittivo e dichiarativo perché è chiaro dove sono in esecuzione le regole.

Se volessimo modellare queste regole in modo tale che risultasse nel sistema di "Entità di dominio sempre valide" avremmo bisogno di suddividere la nostra Customer in Registrar e Reserver entità. E mentre questo potrebbe non sembrare così negativo per questo esempio, poiché le regole diventano più complesse e abbondanti, si finirà con un'esplosione di classi come questa che rappresentano lo stato "all'interno" di un contesto o di un processo. Questo è semplicemente non necessario e creerà inevitabilmente problemi quando due di questi oggetti dipendono dalla stessa porzione di stato.

Inoltre, qualcosa come Customer c = new Customer() è un brutto posto per generare un'eccezione perché non è chiaro quali siano le regole aziendali applicabili. Il che ci porta alla nostra discussione finale.

Vengo solo ad affermare che ci dovrebbe essere la zero convalida delle regole aziendali che si verificano nei costruttori. La costruzione di oggetti non ha nulla a che fare con il tuo dominio aziendale e, per questo motivo, oltre alle ragioni sopra citate relative al contesto e alla coerenza, tutte le regole aziendali dovrebbero essere applicate all'interno degli organismi del metodo di un'entità (è probabile che i metodi prendano il nome dai processi aziendali ?).

Inoltre, "il rinnovo" di un oggetto è non la stessa cosa della creazione di una nuova entità nel tuo dominio. Gli oggetti non escono dal nulla. Se esistono regole aziendali su come una nuova entità può entrare nel tuo sistema, allora dovrebbe essere modellata nel tuo dominio . Ecco alcune ulteriori discussioni sull'argomento da parte di un vero master link

    
risposta data 12.06.2018 - 21:55
fonte
3

Is a domain object in a valid state if it is initialised with default values using a zero arguement constructor/initialised with empty/null values?

Può essere.

Non c'è niente contro le regole sulla creazione di un oggetto dominio valido usando un costruttore predefinito.

Non c'è niente contro le regole sulla creazione di un oggetto dominio valido con valori vuoti.

Non c'è niente contro le regole sulla creazione di un oggetto dominio valido privo di elementi facoltativi.

Non c'è niente contro le regole sulla creazione di un oggetto dominio valido usando valori null.

Dove si verificano problemi: creazione di oggetti di dominio che non obbediscono alla loro algebra semantica.

L'implementazione corretta dipende da una corretta interpretazione della semantica.

È normale prendere una rappresentazione che perdona di un concetto di dominio e applicare i vincoli aggiuntivi. Questa è una delle aree in cui i linguaggi di implementazione che semplificano l'aggiunta di tipi (es: F #) hanno vantaggi rispetto a linguaggi più scomodi come Java.

Ad esempio, se abbiamo un dominio che si preoccupa dei numeri, e all'interno di quel dominio ci sono alcune funzionalità specifiche dei numeri primi, o primi di Mersenne, allora un meccanismo naturale da usare è creare tre diversi tipi; esplicitando la nozione che ci sono diversi gruppi di vincoli da applicare agli input in diverse parti della soluzione.

Number n = new Number(...)
Optional<Prime> p = n.prime()
if (p.isPresent()) {
    Optional<MersennePrime> mp = p.mersennePrime()
    // ...
} 

In questo tipo di design, la "convalida" che il numero è davvero un Prime esisterebbe all'interno del costruttore Prime stesso. In un contesto in cui abbiamo bisogno del vincolo aggiuntivo che il parametro sia un Mersenne primo, usiamo un Prime - > conversione MersennePrime , assicurando che la conoscenza del vincolo di Mersenne abbia un'autorità ("non ripeterti").

"Rendi implicito, esplicito"

    
risposta data 11.06.2018 - 16:19
fonte

Leggi altre domande sui tag