Ecco la migliore analogia che ho potuto "insegnare" ai miei colleghi quando volevo lavorare su DDD con Value Objects.
Ogni volta che pensi a un oggetto valore, pensa all'oggetto DateTime in .Net.
Ad esempio, quando si dispone di un DateTime nell'oggetto e si mantiene quell'oggetto, si salva solo una rappresentazione in formato stringa della data e dell'ora in questione. Può anche essere formattato per soddisfare determinati locali (mese-giorno o giorno-mese per esempio).
Tuttavia, l'oggetto DateTime è così sofisticato internamente. Hanno più membri per memorizzare valori diversi (giorno, mese, anno, ora, minuto, secondi, millisecondi) in modo che possano eseguire operazioni come .AddDay (2) che fondamentalmente prende il campo giorno, ne aggiunge un 2 e restituisce un nuovissimo oggetto DateTime con la nuova aggiunta. Quindi, non modifichi mai veramente l'oggetto DateTime. Ne crei uno ogni volta che usi uno dei loro metodi.
Lo stesso vale per gli oggetti valore personalizzati ... Dì, CreditCard ... diamo un esempio.
Vogliamo memorizzare le informazioni della carta di credito per un utente. Nel nostro dominio, non è necessario tenere traccia della carta di credito. Quindi, la prima cosa che potremmo essere tentati di fare è creare 4 campi.
string CreditCardNumber {get;set;}
string CreditCardExpiration {get;set;}
string CreditCardType {get;set;}
string CreditCardSecurityCode {get;set;}
Ora dì che abbiamo questo campo nella nostra AggregareRoot / Entity. Se vogliamo "analizzare" il nostro ExpirationDate su un oggetto DateTime miningful (dal 07/18 al 07/01/2018) per scopi di validazione, ora dobbiamo aggiungere quella logica alla nostra AR / Entity. Potresti pensare ... "va bene" ma non lo è, perché le nostre AR / Entità dovrebbero essere focalizzate sul problema che risolvono, non convalidando le date di scadenza della carta di credito. Questo è più lavoro ausiliario.
Immagina se DateTime non avesse tutte quelle funzionalità di manipolazione di Date e Time e hai bisogno di implementare tutto ciò nella tua classe, suona bene? Certo che no.
Quindi, nel nostro esempio creiamo una classe CreditCard
public class CreditCard: ValueObject
quindi trasferiamo tutti questi campi in questa classe (puoi rimuovere il prefisso CreditCard). Ora hai molto potere nelle tue mani. È possibile creare tutti i metodi di convalida desiderati: convalida del checksum, convalida della data di scadenza e analisi, in realtà convalidare che il tipo di carta di credito corrisponda alla sequenza numerica (Mastercard inizia con 54 se non vado errato) ... Voglio dire, puoi aggiungi MOLTA funzionalità alla tua classe CreditCard senza inquinare il tuo AR / Entità. Puoi creare il tuo "formato" personale per mantenere queste informazioni se vuoi. Supponete di sovrascrivere il metodo .ToString () e formattarlo come "MC 5474 3434 8383 4848 07/18". Oppure puoi configurare il tuo ORM per mappare le singole proprietà di sola lettura per quei valori.
Come si usa in modo efficace? Semplice. Supponi di avere una classe PaymentMethod che è una radice aggregata. Vuoi tenere traccia di quel metodo di pagamento perché verrà utilizzato per effettuare i pagamenti, quindi forse vuoi avere una cronologia dei pagamenti collegata a questa classe.
All'interno tutto ciò che devi fare è aggiungere un oggetto valore CreditCard ...
public CreditCard BusinessCard {get;}
Ora in uno dei metodi della tua AR ... dì,
private void Enable()
{
this.IsEnabled = BusinessCard.IsValid();
}
Vedi? Ho spostato la logica della convalida della carta nell'oggetto valore. E indovina cosa? Può essere riutilizzato !!! Puoi effettivamente condividere oggetti valore.
Spero che questo aiuti!