Come progettare i confini aggregati?


10

Vorrei scrivere un'applicazione simile all'e-commerce.

E sai che in applicazioni simili i prodotti potrebbero avere proprietà e caratteristiche diverse. Per simulare tale opportunità ho creato le seguenti entità del modello di dominio:

Categoria : è qualcosa come "elettronica> computer", cioè tipi di prodotti. Le categorie contengono un elenco di proprietà (Elenco <Proprietà>).

Proprietà : entità indipendente che contiene il nome, le unità di misura, il tipo di dati. Ad esempio "nome", "peso", "dimensione dello schermo". La stessa proprietà può avere prodotti diversi.

Prodotto : contiene solo il nome e l'elenco dei valori relativi alle proprietà. Il valore è un oggetto che contiene solo il campo valore e l'id campo della proprietà.

Inizialmente ho deciso di rendere la categoria come un singolo aggregato in questo schema perché, ad esempio, quando aggiungo un nuovo prodotto, devo conoscere tutti i dati relativi alla categoria corrente, comprese le proprietà relative alla categoria corrente ( category.AddNewProduct (prodotto) ). Ma cosa devo fare quando devo solo aggiungere una nuova proprietà che non appartiene a nessuna categoria. Ad esempio, non posso fare questa categoria.AddNewProperty (proprietà) perché dice chiaramente che aggiungiamo la proprietà a una categoria specifica.

Ok il prossimo passo ho deciso di separare la Proprietà in un aggregato separato, ma poi sarà un elenco con entità semplici.

Ovviamente posso creare qualcosa come PropertyAggregate per mantenere un elenco interno di proprietà e regole di acquisto, ma quando aggiungo un prodotto, ho bisogno di avere all'interno della categoria l'intero elenco di proprietà appartenenti a questa categoria per controllare gli invarianti. Ma sono anche consapevole del fatto che mantenere i collegamenti all'interno dell'aggregato su altri aggregati è una cattiva pratica.

Quali sono le opzioni per progettare questo business case?


Potresti fornire un esempio più completo di categoria, proprietà e prodotto? Elettronica o computer sarebbero una categoria, iPhone X sarebbe un esempio di un prodotto e una proprietà sarebbe esattamente cosa? Display da 11 "pollici?
Neil

hai quasi ragione. Ho aggiunto alcuni chiarimenti
Cefei,

Sembra che tu stia guardando la progettazione aggregata esclusivamente dal punto di vista del "contenitore di dati". Potresti anche pensare a casi d'uso della tua applicazione, prendendo in considerazione aspetti transazionali, collaborazione / accesso simultaneo, eventi che si verificano, transizioni di stato, ecc.
guillaume31

Risposte:


7

Nella prospettiva DDD Category, Producte Propertysono entità: corrispondono tutte a oggetti che hanno una propria identità.

Opzione 1: il tuo design originale

Hai creato Categoryla radice di un singolo aggregato. Da un lato, questo ha senso, perché l'aggregato deve garantire coerenza quando i suoi oggetti vengono modificati e Productdeve avere il Propertiessuo Category:

inserisci qui la descrizione dell'immagine

D'altra parte, il singolo aggregato significa che tutti i suoi oggetti sono correlati a una radice che li possiede e tutti i riferimenti esterni devono essere fatti tramite questa radice aggregata. Questo implica che:

  • uno specifico Productappartiene a uno e solo uno Category. Se Categoryviene eliminato, lo sono anche i suoi Products.
  • uno specifico Propertyappartiene a uno e solo uno Category. Altrimenti, se "schermi TV" e "monitor per computer" fossero due categorie, "schermi TV: dimensioni" e "monitor per computer: dimensioni" sarebbero due proprietà diverse.

Il secondo punto non corrisponde alla tua narrativa: " Ma cosa dovrei fare quando devo solo aggiungere un nuovo Propertyche non appartiene a nessuna categoria ". E non è chiaro se lo stesso Propertiespuò essere utilizzato in diversi Categories.

Opzione 2: proprietà esterna all'aggregato

Se un Propertyesiste indipendentemente dal Categories, deve essere al di fuori dell'aggregato. E lo stesso se vuoi condividere Propertiestra Categories(che ha senso per altezza, larghezza, dimensioni, ecc ...). Questo sembra definitivamente il caso.

La conseguenza è sul collegamento tra Propertye cose che appartengono all'aggregato: mentre puoi navigare dall'interno dell'aggregato a Property, non ti è più permesso di passare direttamente da Propertya ai valori corrispondenti. Questa restrizione di navigabilità può essere mostrata in un diagramma UML:

inserisci qui la descrizione dell'immagine

Nota che questo design non ti impedisce di avere un List<Property>in Category, con un riferimento semantico (ad esempio java): ogni riferimento nell'elenco si riferisce a un Propertyoggetto condivisibile in un repository.

L'unico problema con questo disegno è che potresti cambiarlo Propertyo cancellarlo: poiché è al di fuori dell'aggregato, l'aggregato non può prendersi cura della coerenza dei suoi invarianti. Ma questo non è un problema. È la conseguenza dei principi DDD e della complessità del mondo reale. Ecco una citazione di Eric Evans nel suo libro fondamentale " Domain-Driven Design: Affrontare la complessità nel cuore del software ":

Non ci si aspetta che le regole che abbraccia AGGREGATES siano sempre aggiornate. Tramite l'elaborazione degli eventi, l'elaborazione batch o altri meccanismi di aggiornamento, è possibile risolvere altre dipendenze entro un determinato periodo di tempo. Ma gli invarianti applicati all'interno di un AGGREGATO verranno applicati con il completamento di ciascuna transazione.

Quindi sì, se modifichi un Property, dovrai assicurarti che un servizio controlli le Categorie ad esso riferite siano aggiornate secondo necessità.

Opzione 3: categoria, proprietà e prodotto in diversi aggregati

Mi chiedo solo se il presupposto che un Productappartiene a un singolo Categorysia fondato:

  • Vedo spesso negozi online che propongono uno Productsotto diversi Categories. Ad esempio, nella categoria "Computer portatili" e nella categoria "Computer" troverai "Laptop Marca X Modello Y" e una "stampante multifunzione Z" nelle categorie "stampante", "scanner" e "fax".
  • Non è possibile che qualcuno crei un Productprimo e solo successivamente lo assegni a Categorie e riempia i valori?
  • Se desideri dividere una categoria, elimineresti davvero i suoi prodotti e poi li ricreare nelle nuove categorie?

Non semplifica gli aggregati e avresti ancora più regole che abbracciano gli aggregati. Ma il tuo sistema sarebbe molto più a prova di futuro.


Grazie mille questa è una spiegazione molto utile. Vorrei chiarire alcuni punti. Vorrei iniziare con la seconda opzione e chissà, forse verrò alla terza. Se Propertysupero i limiti Categorydell'aggregato, ciò significa che Propertydiventa un aggregato in sé e ha bisogno di un repository? Se è vero, allora come passare richiesto List<Property>in Categoryistanza? Attraverso il costruttore? Sarà giusto? E come faccio a scoprire l'elenco di PropertyID di un Categorynon ancora creato?
Cefei,

@zetetic in breve: sì, avrai bisogno di un repository di proprietà indipendente. O si passa un elenco di Proprietà esistenti alla fabbrica della Categoria oppure si creano Categorie vuote e si popola l'elenco con un metodo addProperty. Domanda in cambio: immagina di voler avere proprietà "obbligatorie" e "opzionali" e la caratteristica obbligatoria dipende dalla categoria. Come lo gestiresti?
Christophe,

Per rispondere alla tua domanda, la prima cosa che mi viene in mente è che posso creare un'entità speciale Featuree questa apparterrà solo a Product. e questa entità non parteciperà alla ricerca. che ne dici ?
Cefei,

@zetetic perché no! Avrei lasciato i valori così come sono attualmente nel prodotto e avrei associato la funzione alla categoria. Una categoria ha n caratteristiche (parte del suo aggregato), una Proprietà definisce m Caratteristiche (ma il collegamento passa tramite la categoria Categoria->). Hai quindi scomposto la relazione molti-in-molti in elementi più gestibili, chiarendo il confine aggregato. Infine sull'iniezione del repository: questo non è necessario se fai riferimento ad altri aggregati per identità (leggi questo articolo informit.com/articles/article.aspx?p=2020371&seqNum=4 )
Christophe,

5

A mio avviso, puoi risolverlo in due modi:

La categoria è un tipo speciale di prodotto

Ciò significa che per ogni dato prodotto nel database, contiene una chiave esterna che punta allo stesso prodotto tabella. Un prodotto è un prodotto solo se non esistono prodotti la cui chiave esterna è uguale all'ID di detto prodotto. In altre parole, se non contiene prodotti, è un prodotto.

Ciò semplificherebbe un po 'le cose. I prodotti per le proprietà avrebbero una relazione uno-a-molti e quindi anche le tue categorie hanno una relazione uno-a-molti in quanto sono anche prodotti. Aggiungere una proprietà a una categoria sarebbe facile come aggiungere una proprietà a un prodotto nel tuo programma. Caricare tutte le proprietà significherebbe combinare le proprietà del prodotto con le proprietà del prodotto della categoria associata e fino a raggiungere un prodotto della categoria senza parent.

La tua applicazione di e-commerce dovrebbe fare questa distinzione, ma se è probabile che tu carichi comunque prodotti di una categoria, non è una perdita di prestazioni sapere se hai a che fare con una categoria o un prodotto. Si presta anche bene alla ricerca nella struttura ad albero a livello di prodotto poiché ogni prodotto (categoria) si aprirà a un elenco di sottoprodotti senza molto lavoro aggiuntivo.

Il rovescio della medaglia di questo è ovviamente l'informazione extra presente nel prodotto che non ha senso per una categoria creerebbe campi inutilizzati imbarazzanti nel prodotto. Sebbene questa soluzione sia più flessibile nella tua applicazione, è anche un po 'meno intuitiva.

Rapporto molti-a-molti

I prodotti non hanno più una relazione composita con la proprietà. Si crea una tabella ProductProperty con chiavi esterne sia della tabella dei prodotti che della tabella delle proprietà che collegano i due. Allo stesso modo, si dispone di una tabella delle categorie con una relazione molti-a-molti con la tabella delle proprietà e una tabella CategoryProperty con chiavi esterne sia della tabella delle categorie che della tabella delle proprietà.

Il prodotto stesso avrebbe una relazione molti-a-uno con la categoria, consentendo in sostanza di creare un elenco di proprietà uniche relative sia al prodotto che alla categoria attraverso un'istruzione select ben formalizzata.

Dal punto di vista del database, questo è decisamente più pulito e flessibile. La tua applicazione potrebbe probabilmente funzionare per la maggior parte senza occuparsi direttamente di CategoryProperty o ProductProperty se la query viene eseguita correttamente. Tuttavia, non dovresti trattare la categoria o il prodotto come proprietario della proprietà. Dovrebbe essere una sua entità all'interno del tuo programma. Ciò significa anche che la gestione di dette proprietà sarebbe una questione di creazione della proprietà stessa, quindi di associazione con una categoria o un prodotto in due fasi separate. Certamente più lavoro rispetto alla prima soluzione, ma non è affatto più difficile.

Oltre a questo, dovresti anche eseguire un controllo aggiuntivo alla cancellazione della categoria o del prodotto se una delle sue proprietà viene utilizzata da altri (a differenza della prima soluzione in cui potresti eliminare in modo sicuro tutte le proprietà associate di un determinato prodotto / categoria) .

Conclusione

In un contesto professionale, farei il miglio in più e la categoria di distanza dal prodotto e il prodotto dalla proprietà usando l'approccio molti-a-molti. Non vi sarebbe alcuna possibilità di sovrapposizione di dati e, in un certo senso, è più facile ragionare su ognuna di queste tre come una propria entità. Tuttavia, la prima soluzione non è affatto una cattiva in quanto consente anche di scrivere un'applicazione più semplice. Sappi solo che se pensavi di dover eventualmente passare da una soluzione all'altra, probabilmente sarebbe nel tuo interesse scegliere la seconda.

In bocca al lupo!


Grazie per la risposta dettagliata e interessante! a livello di database ho già modellato come spiegato nel secondo caso, questo modello si chiama entità-attributo-valore ma sono bloccato a livello di codice, vale a dire la definizione di aggregati. Nella maggior parte dei casi, tutte queste entità vengono utilizzate insieme. è possibile combinare in un unico aggregato ma ci sono casi che come il riempimento di directory che, in un certo senso, sono eliminate dall'aggregato.
Cefei,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.