Во-первых, вы должны использовать интерфейсы для получения ссылок на ваш совокупный корень (т. Е. Order ()). Используйте шаблон Factory для создания нового экземпляра Aggregate Root (т. Е. Order ()).
С учетом вышесказанного методы вашего Aggregate Root контролируют доступ к связанным объектам, а не к себе. Кроме того, никогда не раскрывайте сложные типы как общедоступные для агрегатных корней (т. Е. Коллекции IList Lines (), которую вы указали в примере). Это нарушает закон decremeter (sp ck), который гласит, что вы не можете "Dot Walk" свой путь к методам, таким как Order.Lines.Add ().
Кроме того, вы нарушаете правило, позволяющее клиенту получать доступ к ссылке на внутренний объект в совокупном корне. Агрегированные корни могут возвращать ссылку на внутренний объект. Пока внешнему клиенту не разрешено хранить ссылку на этот объект. То есть, ваш «OrderLine» вы передаете в RemoveLine (). Вы не можете позволить внешнему клиенту управлять внутренним состоянием вашей модели (т. Е. Order () и его OrderLines ()). Поэтому следует ожидать, что OrderLine будет новым экземпляром, который будет действовать соответственно.
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);
}
}
}
Не забудьте свою фабрику по созданию новых экземпляров Order ():
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.
}
}
Ваш внешний клиент имеет право на прямой доступ к IOrderLinesRepository через интерфейс для получения ссылки на объект значения в вашем Агрегированном корне. Но я пытаюсь заблокировать это, заставляя мои ссылки использовать все методы Aggregate Root. Таким образом, вы можете пометить описанный выше IOrderLineRepository как внутренний, чтобы он не отображался.
На самом деле я группирую все свои совокупные коренные создания на несколько Фабрик. Мне не понравился подход: «Некоторые совокупные корни будут иметь фабрики для сложных типов, другие не будут». Гораздо проще иметь ту же логику, которой следуют на протяжении всего моделирования предметной области. «О, так что Sales () является агрегатным корнем, подобным Order (). Для него тоже должна быть фабрика».
И последнее замечание: если у вас есть комбинация, то есть SalesOrder (), которая использует две модели Sales () и Order (), вы бы использовали Сервис для создания и использования этого экземпляра SalesOrder (), так как Совокупные корни Sales () или Order (), а также их репозитории или фабрики контролируют сущность SalesOrder ().
Я очень, очень рекомендую эту бесплатную книгу Абеля Аврама и Флойда Маринеску о дизайне доменного диска (DDD), так как она прямо отвечает на ваши вопросы, напечатанной крупным шрифтом на 100 страниц. Наряду с тем, как разделить сущности вашего домена на модули и тому подобное.
Редактировать: добавлено больше кода