2009-03-16 2 views
7

Похоже, что я немного запутался в этом бизнесе DDD \ LinqToSql. Я создаю систему, использующую POCOS и linq для sql, и у меня есть репозитории для совокупных корней. Так, например, если у вас были классы Order-> OrderLine, у вас есть репозиторий для Order, но не OrderLine, так как Order является корнем из совокупности. В репозитории есть метод удаления для удаления заказа, но как вы удаляете OrderLines? Вы бы подумали, что у вас есть метод Order, который называется RemoveOrderLine, который удалил строку из коллекции OrderLines, но также необходимо удалить OrderLine из базовой таблицы l2s. Поскольку для OrderLine нет репозитория, как вы должны это делать?Управление доменом (Linq to SQL) - Как удалить части агрегата?

Возможно, существуют специализированные публичные репортажи для запросов к корням и внутренним общим хранилищам, которые фактически используют объекты домена для удаления данных в агрегатах?

public class OrderRepository : Repository<Order> { 
    public Order GetOrderByWhatever(); 
} 

public class Order { 
    public List<OrderLines> Lines {get; set;} //Will return a readonly list 
    public RemoveLine(OrderLine line) { 
     Lines.Remove(line); 
     //************* NOW WHAT? *************// 
     //(new Repository<OrderLine>(uow)).Delete(line) Perhaps?? 
     // But now we have to pass in the UOW and object is not persistent ignorant. AAGH! 
    } 
} 

Я хотел бы знать, что другие люди сделали, как я не могу быть только один борется с этим .... Я надеюсь .... Спасибо

ответ

2

Вы называете RemoveOrderLine на заказ, который вызовите соответствующую логику. Это не включает внесение изменений в сохраненную версию.

Позже вы вызываете метод сохранения/обновления в репозитории, который получает измененный порядок. Конкретная задача становится в знании того, что изменилось в объекте домена, который существует несколько вариантов (я уверен, что есть больше, чем те, которые я список):

  • Имея объект домена отслеживать изменения, которые будет включать отслеживание того, что x необходимо удалить из строк заказа. Нечто похожее на отслеживание сущности также может быть учтено.
  • Загрузите сохраненную версию. Имейте код в репозитории, который распознает различия между постоянной версией и версией в памяти и запускает изменения.
  • Загрузите сохраненную версию. Имейте код в корневом агрегате, который получает вам различия, учитывая исходный корневой агрегат.
+0

Я предполагаю, что это расширение означает, что вы не используете ORM непосредственно на своих объектах домена. Я надеялся, что смогу избежать дополнительной работы, описанной выше, при условии, что linq to sql автоматически сохранит изменения, внесенные в объект домена ... –

+0

y, это настоящая проблема. Я делаю это. Я вижу комментарии все время на Nhibernate, поддерживая более сложные сценарии, но я не смотрел, как он играет с этим типом сценария (который не просто использует POCOs) – eglasius

+0

Спасибо; хотя и не ужасно идеальный (ошибка LTS, а не ваш ответ), похоже, это подтверждает мои собственные догадки о том, как мне нужно это делать ... – Funka

1

Во-первых, вы должны разоблачить интерфейсы, чтобы получить ссылки на ваш сводный корень (т. Е. Порядок()). Используйте шаблон Factory для создания нового экземпляра корня агрегата (т. Е. Порядка()).

С учетом сказанного, методы, используемые в вашем сводном корне, позволяют получить доступ к связанным с ним объектам - а не к самому себе. Кроме того, никогда не выставляйте сложные типы как общедоступные по совокупным корням (т. Е. Коллекция Lines() IList, указанная в примере). Это нарушает закон decremeter (sp ck), который говорит, что вы не можете «Dot Walk» перейти к методам, таким как Order.Lines.Add().

А также вы нарушаете правило, которое позволяет клиенту получить доступ к ссылке на внутренний объект на сводном корне. Совокупные корни могут возвращать ссылку внутреннего объекта. Пока внешнему клиенту не разрешается ссылаться на этот объект. I.e., ваш «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); 
    } 
    } 
} 

Не забыли свой завод для создания новых экземпляров ордена():

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() Aggregate Roots, а также их репозитории или фабрики, собственный контроль над объектом SalesOrder().

Я очень рекомендую this free book от Abel Avram и Floyd Marinescu по дизайну Domain Drive Design (DDD), так как он напрямую отвечает на ваши вопросы, в печати на 100 страниц большой печати. Наряду с тем, как больше разделить ваши объекты домена на модули и т. Д.

Edit: добавлено больше кода

+0

Спасибо за ответ.Однако я не уверен в том, что поведение строки заказа выставляется через методы порядка. Кажется, что GetOrderLineVatAmount должен быть на линии, иначе вы должны передать строку в функцию в заказе. Кроме того, почему заказ должен иметь ссылку на OrderRepos? –

+0

Все зависит от того, как вы видите «OrderLine». Приведенный выше код отражает подход «Объект ценности». Объект Value отличается от объекта в том смысле, что он не имеет идентификатора. У каждого OrderLine есть Identity? Если да, то действительно, если вы думаете об этом? – eduncan911

+0

OrderLine.LineNumber OrderLine.Description OrderLine.Cost OrderLine.PartNumber Определение Объекта значения здесь не совпадает с идентификатором; но, суммирование его значений. Но, как я отметил выше в ответе, вы можете открыть IOrderLineRepository(), и ваш удаленный клиент имеет права доступа. – eduncan911

0

В продолжение .... я перешел к использованию NHibernate (а не ссылки на SQL), но в действительности вы не нужны операции РЕПО для OrderLine. Если вы просто удалите OrderLine из коллекции в порядке, он просто удалит OrderLine из базы данных (если вы правильно сделали свое сопоставление). Поскольку я заменяю репозитории в памяти, если вы хотите найти определенную строку заказа (не зная родительского порядка), вы можете написать linq для запроса nhibernate, который связывает порядок с orderline, где orderlineid = значение. Таким образом, он работает при запросе из db и из памяти. Хорошо, что вы идете ...

+0

Bummer, Я действительно надеялся, что кто-то лучше ответит на ваш (очень отличный и очень уместный) вопрос, не отпуская Linq до Sql, чтобы это сделать! – Funka

1

После борьбы с этой точной проблемой я нашел решение. Посмотрев, что создает разработчик с l2sl, я понял, что решение находится в двухсторонних связях между порядком и упорядоченной строкой. В заказе есть много порядковых номеров, а порядок заказов имеет один заказ. Решение состоит в использовании двухсторонних ассоциаций и атрибута сопоставления, называемом DeleteOnNull (который вы можете использовать для полной информации о Google). Последнее, чего мне не хватало, было то, что ваш класс сущности должен регистрироваться для добавления и удаления событий из L2s-сущности. В этих обработчиках вы должны установить для ассоциации Order в строке заказа значение null. Вы можете увидеть пример этого, если вы посмотрите на какой-то код, создаваемый конструктором l2s.

Я знаю, что это расстраивает, но после нескольких дней борьбы с ним у меня это работает.

Смежные вопросы