In primo luogo, è necessario esporre le interfacce per ottenere riferimenti alla radice di aggregazione (ad esempio Order()). Usa il pattern Factory per rinnovare una nuova istanza della radice di aggregazione (ad esempio Order()).
Con ciò detto, i metodi sul tuo Radice di aggregazione compongono l'accesso ai relativi oggetti correlati, non a se stesso. Inoltre, non esporre mai tipi complessi come pubblici sulle radici aggregate (cioè la raccolta IList di Lines() che hai specificato nell'esempio). Ciò viola la legge del decremento (sp ck), che dice che non è possibile "Dot Walk" per i metodi, come Order.Lines.Add().
E inoltre, si viola la regola che consente al client di accedere a un riferimento a un oggetto interno su una radice di aggregazione. Le radici aggregate possono restituire un riferimento di un oggetto interno. Finché, il client esterno non può tenere un riferimento a quell'oggetto. Ad esempio, la tua "OrderLine" si passa a RemoveLine(). Non è possibile consentire al client esterno di controllare lo stato interno del modello (ad esempio Order() e il suo OrderLines()). Pertanto, dovresti aspettarti che OrderLine sia una nuova istanza su cui agire di conseguenza.
public interface IOrderRepository
{
Order GetOrderByWhatever();
}
internal interface IOrderLineRepository
{
OrderLines GetOrderLines();
void RemoveOrderLine(OrderLine line);
}
public class Order
{
private IOrderRepository orderRepository;
private IOrderLineRepository orderLineRepository;
internal Order()
{
// constructors should be not be exposed in your model.
// Use the Factory method to construct your complex Aggregate
// Roots. And/or use a container factory, like Castle Windsor
orderRepository =
ComponentFactory.GetInstanceOf<IOrderRepository>();
orderLineRepository =
ComponentFactory.GetInstanceOf<IOrderLineRepository>();
}
// you are allowed to expose this Lines property within your domain.
internal IList<OrderLines> Lines { get; set; }
public RemoveOrderLine(OrderLine line)
{
if (this.Lines.Exists(line))
{
orderLineRepository.RemoveOrderLine(line);
}
}
}
non dimenticate la vostra fabbrica per la creazione di nuove istanze dell'Ordine():
public class OrderFactory
{
public Order CreateComponent(Type type)
{
// Create your new Order.Lines() here, if need be.
// Then, create an instance of your Order() type.
}
}
tuo client esterno non ha il diritto di accedere direttamente al IOrderLinesRepository, attraverso l'interfaccia per ottenere un riferimento di un oggetto valore all'interno della radice di aggregazione. Ma cerco di bloccarlo forzando i miei riferimenti a tutti i metodi della Radice Aggregata. Pertanto, è possibile contrassegnare l'IOrderLineRepository come interno in modo che non sia esposto.
In realtà raggruppo tutte le mie creazioni di radice aggregata in più fabbriche. Non mi piaceva l'approccio di "Alcune radici aggregate avranno fabbriche per tipi complessi, altre no". Molto più facile avere la stessa logica seguita in tutta la modellazione del dominio. "Oh, così Sales() è una radice aggregata come Order(). Ci deve essere una fabbrica anche per questo."
Un'ultima nota è che se si dispone di una combinazione, ad esempio SalesOrder(), che utilizza due modelli di Sales() e Order(), si utilizzerà un servizio per creare e agire su quell'istanza di SalesOrder() poiché né le diramazioni aggregate Sales() o Order(), né i relativi repository o factory, hanno il controllo sull'entità SalesOrder().
Consiglio vivamente lo this free book da Abel Avram e Floyd Marinescu su Domain Drive Design (DDD) perché risponde direttamente alle vostre domande, in una stampa di 100 pagine di dimensioni ridotte. Insieme a come disaccoppiare ulteriormente le entità di dominio in moduli e così via.
Edit: aggiunte più codice
Presumo che questa spiegazione indichi che non stai utilizzando un ORM direttamente sulle entità del tuo dominio. Speravo di essere in grado di evitare il lavoro extra che descrivi sopra, avendo linq per SQL persistere automaticamente le modifiche apportate all'oggetto dominio ... –
y, questo è il vero problema. Ho fatto quanto sopra. Vedo sempre commenti su Nhibernate che supporta scenari più avanzati, ma non ho visto come funziona con questo tipo di scenario (che non è solo l'uso di POCO) – eglasius
Grazie; anche se non terribilmente ideale (colpa di LTS, non la tua risposta) questo sembra confermare le mie stesse intuizioni su come avrei bisogno di fare questo ... – Funka