Domanda

Questa è una domanda pratica di progettazione guidata dal dominio:

Concettualmente, penso di ottenere le radici aggregate fino a quando non vado a definirne una.

Ho un'entità Employee, che è emersa come radice aggregata. Nell'azienda, alcuni dipendenti possono avere delle violazioni relative al lavoro registrate contro di loro:

dipendenti ----- * Violazioni

Dal momento che non tutti i dipendenti sono soggetti a questo, penso che le violazioni non facciano parte dell'aggregato dei dipendenti, giusto?

Quindi, quando voglio lavorare con i Dipendenti e le loro relative violazioni, queste due interazioni del Repository sono separate da un Servizio?

Infine, quando aggiungo una violazione, quel metodo è sull'entità dipendente? Grazie per l'aiuto!

È stato utile?

Soluzione

Dopo aver fatto ancora PIÙ ricerche, penso di avere la risposta alla mia domanda.

Paul Stovell ha avuto questa risposta leggermente modificata a una domanda simile sulla messageboard DDD . Sostituisci & Quot; Cliente & Quot; per " Employee " ;, e " Ordine " per " Violazione " e ottieni l'idea.

  

Solo perché il cliente fa riferimento all'ordine   non significa necessariamente che l'ordine cada   all'interno della radice aggregata del cliente.   Gli indirizzi del cliente potrebbero, ma   gli ordini possono essere indipendenti (per   esempio, potresti avere un servizio che   elabora tutti i nuovi ordini, indipendentemente da chi   il cliente è. Dover andare   Cliente - & Gt; Gli ordini non hanno senso   questo scenario).

     

Dal punto di vista del dominio, puoi   anche mettere in dubbio la validità di quelli   riferimenti (il cliente ha riferimento a   un elenco di ordini). Quanto spesso lo farai   ha effettivamente bisogno di tutti ordini per a   cliente? In alcuni sistemi lo fa   senso, ma in altri, un cliente   potrebbe fare molti ordini. Le possibilità sono   vuoi ordini per un cliente tra   un intervallo di date o ordini per un cliente   che non sono ancora stati elaborati o ordini   che non sono stati pagati e così via.   Lo scenario in cui avrai bisogno di tutto   di loro potrebbe essere relativamente raro.   Tuttavia, è molto più probabile che   quando si tratta di un Ordine, lo farai   desidera le informazioni del cliente. Quindi dentro   codice, Order.Customer.Name è utile,   ma Customer.Orders[0].LineItem.SKU -   probabilmente non così utile. Ovviamente,   dipende totalmente dalla tua attività   dominio.

In altre parole, l'aggiornamento del cliente non ha nulla a che fare con l'aggiornamento degli ordini. E ordini, o violazioni nel mio caso, potrebbero essere concepiti indipendentemente da clienti / impiegati.

Se le violazioni avessero linee di dettaglio, allora la linea di violazione e violazione farebbe parte dello stesso aggregato perché la modifica di una linea di violazione avrebbe probabilmente un impatto su una violazione.

EDIT ** La ruga qui nel mio dominio è che le violazioni non hanno alcun comportamento. Sono fondamentalmente registrazioni di un evento accaduto. Non sono ancora sicuro delle implicazioni che ha.

Altri suggerimenti

Eric Evan afferma nel suo libro Domain-Driven Design: affrontare la complessità nel cuore del software ,

  

Un AGGREGATO è un cluster di oggetti associati che trattiamo come un'unità ai fini della modifica dei dati .

Ci sono 2 punti importanti qui:

  1. Questi oggetti devono essere trattati come " unit " ;.
  2. Ai fini di " modifica dei dati " ;.

Credo nel tuo scenario, Employee e Violation non siano necessariamente un'unità insieme, mentre nell'esempio di Order e OrderItem fanno parte di una singola unità.

Un'altra cosa che è importante per modellare i confini aggregati è se hai degli invarianti nel tuo aggregato. Gli invarianti sono regole commerciali che dovrebbero essere valide tra & Quot; interi & Quot; aggregato. Ad esempio, come nell'esempio Order and OrderItem, potresti avere un invariante che afferma che il costo totale dell'ordine dovrebbe essere inferiore a un importo predefinito. In questo caso, ogni volta che si desidera aggiungere un OrderItem all'Ordine, questo invariante deve essere applicato per assicurarsi che il proprio Ordine sia valido. Tuttavia, nel tuo problema, non vedo invarianti tra le tue entità: Dipendente e Violazione.

Risposta così breve:

Credo che Employee and Violation appartengano ciascuno a 2 aggregati separati. Ognuna di queste entità è anche la propria radice aggregata. Quindi hai bisogno di 2 repository: EmployeeRepository e ViolationRepository.

Credo anche che dovresti avere un'associazione unidirezionale da Violazione a Dipendente. In questo modo, ogni oggetto di violazione sa a chi appartiene. Ma se vuoi ottenere l'elenco di tutte le violazioni per un determinato dipendente, puoi chiedere al repository delle violazioni:

var list = repository.FindAllViolationsByEmployee(someEmployee);

Dici di avere entità dipendente e violazioni e ogni violazione non ha alcun comportamento in sé. Da quello che posso leggere sopra, mi sembra che potresti avere due radici aggregate:

  • dipendenti
  • EmployeeViolations (chiamalo EmployeeViolationCard o EmployeeViolationRecords)

EmployeeViolations è identificato dallo stesso ID dipendente e contiene una raccolta di oggetti di violazione. In questo modo ottieni comportamenti per dipendente e violazioni separati e non ottieni entità Violazione senza comportamento.

Se la violazione è un'entità o un oggetto valore, è necessario decidere in base alle sue proprietà.

Sono generalmente d'accordo con Mosh su questo. Tuttavia, tieni presente il concetto di transazioni dal punto di vista aziendale. Quindi prendo effettivamente & Quot; ai fini della modifica dei dati & Quot; per indicare " ai fini della transazione / e " ;.

I repository sono viste del modello di dominio. In un ambiente di dominio, queste & Quot; viste & Quot; supportare o rappresentare realmente una funzione o capacità aziendale: una transazione. Caso in questione, il Dipendente può avere una o più violazioni e, in tal caso, sono aspetti di una o più transazioni in un determinato momento. Considera i tuoi casi d'uso.

Scenario: " Un dipendente commette un atto che costituisce una violazione del posto di lavoro. " Questo è un tipo di evento aziendale (ovvero una transazione, o parte di una transazione più ampia, forse distribuita) che si è verificato. L'oggetto dominio interessato alla radice può essere effettivamente visto da più di una prospettiva, motivo per cui è confuso. Ma la cosa da ricordare è il comportamento per quanto riguarda una transazione commerciale, poiché vuoi che i tuoi processi aziendali modellino il mondo reale nel modo più accurato possibile. In termini di relazioni, proprio come in un database relazionale, il tuo modello di dominio concettuale dovrebbe effettivamente indicare questo già (cioè l'associatività), che spesso può essere letto in entrambe le direzioni:

Dipendente < ---- commette un ------- impegnato da ---- > Violazione

Quindi, per questo caso d'uso, sarebbe giusto dire che si tratta di una transazione che tratta delle violazioni e che la radice - o " primary " entità - è una violazione. Questa, quindi, sarebbe la tua radice aggregata a cui faresti riferimento per quella particolare attività o processo aziendale. Ciò non vuol dire che, per un'attività o un processo diverso, non è possibile avere una radice aggregata Dipendente, come ad esempio la & Quot; nuova procedura per i dipendenti & Quot ;. Se ti prendi cura di te, non dovrebbero esserci effetti negativi sui riferimenti ciclici o essere in grado di attraversare il tuo modello di dominio in diversi modi. Avvertirò, tuttavia, che il governo di questo dovrebbe essere pensato e gestito dal controller del tuo dominio aziendale, o qualsiasi equivalente tu abbia.

A parte: pensando in termini di modelli (cioè MVC), il repository è una vista, gli oggetti del dominio sono il modello e quindi si dovrebbe anche usare una qualche forma di modello del controller. In genere, il controller dichiara l'implementazione concreta e l'accesso ai repository (raccolte di radici aggregate).

Nel mondo dell'accesso ai dati ...

Utilizzando LINQ-To-SQL come esempio, DataContext sarebbe il controller che espone una vista delle entità Cliente e Ordine. La vista è un tipo di tabella non dichiarativo, orientato al framework (equivalente approssimativo al repository). Si noti che la vista mantiene un riferimento al controller principale e spesso passa attraverso il controller per controllare come / quando la vista viene materializzata. Pertanto, il controller è il tuo fornitore, che si occupa della mappatura, della traduzione, dell'idratazione degli oggetti, ecc. Il modello è quindi il tuo POCO dei dati. Praticamente un tipico modello MVC.

Usando N / Hibernate come esempio, ISession sarebbe il controller che espone una vista delle entità Cliente e Ordine tramite la sessione. Enumerable (query query) o session.Get (id oggetto) o session.CreateCriteria (typeof (Cliente)). Lista ()

Nel mondo della logica aziendale ...

Customer { /*...*/ }

Employee { /*...*/ }

Repository<T> : IRepository<T>
              , IEnumerable<T>
              //, IQueryable<T>, IQueryProvider //optional

{ /**/ }

BusinessController {
 Repository<Customer>  Customers { get{ /*...*/ }} //aggregate root
 Repository<Order> Orders { get{ /*...*/ }} // aggregate root
}

In poche parole, lascia che i processi e le transazioni aziendali siano la guida e lascia che l'infrastruttura aziendale si evolva naturalmente man mano che i processi / le attività vengono implementati o refactored. Inoltre, preferisci la componibilità rispetto al tradizionale design della scatola nera. Quando sarai orientato ai servizi o al cloud computing, sarai contento di averlo fatto. :)

Mi chiedevo quale sarebbe stata la conclusione?

Le "violazioni" diventano un'entità radice. E le "violazioni" verrebbero citate dall'entità radice "dipendente". ovvero repository di violazioni < - > repository dei dipendenti

Ma sei confuso di rendere le violazioni un'entità radice perché non ha alcun comportamento.

Ma il "comportamento" è un criterio per qualificarsi come entità radice? Non credo.

una domanda leggermente ortogonale per testare la comprensione qui, tornando a Order ... OrderItem esempio, potrebbe esserci un modulo di analisi nel sistema che vuole esaminare direttamente OrderItems cioè ottenere tutti gli OrderItems per un particolare prodotto o tutto l'ordine gli elementi superiori a un determinato valore, ecc., hanno un sacco di casi d'uso come questo e guidano " radice aggregata " all'estremo potremmo sostenere che OrderItem è una radice aggregata diversa in sé ??

Dipende. Qualsiasi modifica / aggiunta / eliminazione di una violazione modifica qualsiasi parte del dipendente - ad es. stai memorizzando il conteggio delle violazioni o il conteggio delle violazioni negli ultimi 3 anni a carico del dipendente?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top