Valori validi delle proprietà su Roots aggregati controllati da altri domini

1

Attualmente stiamo iniziando un aggiornamento del nostro sistema e abbiamo deciso di seguire la metodologia DDD. Il sistema è altamente personalizzabile dagli utenti per consentire loro di adattare il software alle loro esigenze specifiche. Ciò ha portato ad una certa confusione sul modo corretto di implementare le proprietà su alcune delle nostre radici aggregate.

Diciamo che abbiamo il provider AR

public sealed class Provider : IAggregateRoot
{
    public Guid Id { get; private set; }
    public int Version { get; private set; }

    public Name Name { get; private set; }
    // Other value objects
}

Ora, dobbiamo aggiungere una proprietà relativa alla razza del Fornitore. Le scelte valide per l'impostazione sono controllate dall'utente. Qui è dove sorgono le domande.

Ci saranno alcune aree dell'app che controllano queste impostazioni, quindi per ora ci riferiamo ad essa come Settings .

Le razze valide inserite dall'utente in Settings sembrano essere oggetti valore, ma sono?

  1. Dal punto di vista dell'identità, in realtà non ne hanno uno. Sono completamente definiti dai loro valori, quindi valgono oggetti.
  2. Se Race è un oggetto valore, dobbiamo semplicemente renderlo un oggetto valore nei domini che utilizzano il valore Race e mantenerlo coerente con le modifiche in Settings ?
  3. Se Race non è un oggetto valore e invece un Entity con un ID, manterremo un riferimento ID nella radice aggregata Provider ? È persino valido mantenere un riferimento a un'entità che non è una radice aggregata in un altro dominio?

Siamo propensi a renderli oggetti di valore per il momento, ma vogliamo essere sicuri che sia la strada giusta da percorrere prima di andare avanti. Abbiamo molti di questi intervalli definiti dall'utente per varie proprietà e, una volta scelta la strada da percorrere, vogliamo mantenerla coerente.

In un'area di indagine correlata, quando eseguiamo la convalida su un nuovo Provider , vogliamo verificare che il valore scelto sia un valore valido in quell'istanza. A quel punto usiamo semplicemente i metodi di comunicazione del dominio per richiedere un elenco di valori di Race validi, quindi trasferirli nella logica di creazione dell'AR per scopi di convalida?

    
posta Bradford Dillon 01.03.2018 - 02:09
fonte

1 risposta

1

Iniziamo con una breve discussione su Entità, Aggregate Root, Value Objects e identità in quanto una certa confusione qui sembra essere un grande contributo al tuo problema.

Le entità sono oggetti che hanno un concettuale ciclo di vita (da non confondere con un ciclo di vita dell'oggetto). A causa del suddetto comportamento concettuale , ci deve essere un modo per distinguere l'uno dall'altro. Come è fatto è puramente un dettaglio di implementazione del sistema. Spesso, poiché un RDBMS viene scelto come backing store per il sistema, Entities conterrà un attributo arbitrario Id che consente al sistema di distinguere l'uno dall'altro. In altri casi, può esistere una chiave naturale (attraverso uno o più attributi in combinazione). Ma ancora, in che modo due entità sono distinte è un dettaglio di implementazione.

I Roots aggregati sono semplicemente Entità che richiedono ad altre Entity di far valere i loro invarianti. Per questo motivo, devono incapsulare l'accesso / la modifica a quelle altre Entità per coerenza transazionale. Dal "fuori" non vi è alcuna differenza (e spesso nessuna caratteristica distintiva) tra un'entità e una radice aggregata. Un cliente di un sistema non deve sapere se sta lavorando con un'entità o una radice di aggregazione.

Questo ci porta a Value Objects. Un oggetto valore non ha un'identità concettuale , nessun ciclo di vita e può, pertanto, essere creato e distrutto con facilità. Quanto sopra non significa un Oggetto Valore non ha un'identità fisica (una chiave primaria nei termini RDBMS), piuttosto che questa identità è nascosta dal tuo dominio. Dopo tutto, potrebbe comunque essere necessario memorizzare / correlare i dati per scopi di normalizzazione.

Da quanto sopra ora possiamo vedere che è il comportamento di un oggetto che lo classifica come Entity o Value Object, non alcune analisi dei dati che l'oggetto contiene (dettagli di implementazione). Ad esempio, Address è l'esempio per eccellenza di un oggetto dominio che confonde spesso gli sviluppatori sul fatto che debba essere trattato come oggetto valore o entità. Dopo tutto, l'intero punto di un Address deve essere unico, giusto? Per questo motivo, Address verrà spesso considerato come oggetto (tabella) dal backing store e semplicemente correlato a Persons (o qualsiasi altra cosa) secondo necessità (più Persons può condividere il stesso Address giusto?). Questa decisione di implementazione è dove inizia la confusione. Sebbene esista un identificatore univoco per ogni Address , ancora deve essere trattato come un oggetto valore. Quando Person cambia il loro Address , non modifichiamo nessun campo, noi li assegniamo semplicemente uno nuovo (dal nostro elenco di Addresses ).

Ora che qui abbiamo trattato alcuni aspetti di ciò che costituisce ciascuno di questi elementi costitutivi, vediamo come si applicano al tuo oggetto Race . Il tuo concetto di Race ha un ciclo di vita? Probabilmente no. Dovrebbe essere un oggetto valore. Indipendentemente dal fatto che si possa o meno distinguere da altri Races è irrilevante.

La vera domanda qui è perché più domini devono conoscere queste informazioni? Cercando di unire insieme alcuni sistemi di Domain Events (ad esempio ProviderChangedRace ) per mantenere le informazioni coerenti in più contesti, si crea un bel po 'di complessità aggiuntiva e punti di errore.

La pietra miliare del DDD è che i sistemi sono progettati attorno al comportamento (non ai dati), e spesso è un modello carente che crea questo tipo di imbarazzo, non una regola tecnica mancante /dettaglio. C'è un modo per organizzare il tuo sistema in modo tale che ogni oggetto che ha bisogno di usare Race possa essere refactored in una singola Entity / Aggregate? Modificando il tuo sistema in modo che l'oggetto che usa Race sia lo stesso oggetto che modifica Races disponibile, puoi evitare completamente molti dei problemi sollevati nella tua domanda. Ad esempio, il tuo Provider potrebbe essere caricato con Races disponibile e Race scelto. Ora aggiungendo nuove opzioni e facendo nuove scelte sono accoppiati in un unico confine transazionale dove l'applicazione di invarianti è banale. In una nota simile, non si "fa" la convalida su Provider , a Provider dovrebbe essere valida se stessa.

Capisco che ci possono essere più pezzi per questo puzzle, ma il refactoring per una comprensione più profonda è qualcosa che dovrebbe essere considerato qui. Prova a trovare un modo per "affettare" i tuoi dati verticalmente in modo da riunire comportamento .

    
risposta data 14.03.2018 - 17:21
fonte

Leggi altre domande sui tag