Подтвердить что ты не робот

Шаблон хранилища без ORM

Я использую шаблон репозитория в приложении .NET С#, которое не использует ORM. Однако проблема заключается в том, как заполнить свойства списка "один ко многим" объекта. например если у клиента есть список заказов, то есть если класс Customer имеет свойство List под названием Orders, а в моем репозитории есть метод GetCustomerById, то?

  • Должен ли я загружать список заказов в методе GetCustomerById?
  • Что делать, если сам заказ имеет другое свойство списка и т.д.?
  • Что делать, если я хочу делать ленивую загрузку? Где я могу поместить код для загрузки свойства Orders в клиенте? Внутри свойства Orders get {} accessor? Но тогда мне придется вводить репозиторий в объект домена? который, я думаю, не является правильным решением.

Это также вызывает вопросы для таких функций, как отслеживание изменений, удаление и т.д.? Итак, я думаю, что конечным результатом я могу сделать DDD без ORM?

Но сейчас меня интересует только ленивая загрузка свойств списка в моих доменах? Любая идея?

Набиль

Я предполагаю, что это очень распространенная проблема для тех, кто не использует ORM в проекте Driven Design? Любая идея?

4b9b3361

Ответ 1

Можно ли делать DDD без ORM?

Да, но ORM упрощает вещи.

Честно говоря, я думаю, что ваша проблема не связана с необходимостью ORM или нет - это то, что вы слишком много думаете о данных, а не о поведении, которое является ключом к успеху с DDD. В терминах модели данных большинство объектов будут иметь ассоциации с большинством других объектов в той или иной форме, и с этой точки зрения вы можете пересечь все вокруг модели. Это то, что выглядит с вашим клиентом и заказами, и, возможно, почему вы считаете, что вам нужна ленивая загрузка. Но вам нужно использовать агрегаты, чтобы разбить эти отношения на поведенческие группы.

Например, почему вы смоделировали агрегат клиента, чтобы иметь список заказов? Если ответ "потому что клиент может иметь заказы", ​​то я не уверен, что вы в мышлении DDD.

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

Я подозреваю, что Клиент должен быть отдельным агрегатом без списка заказов, а Заказ должен быть агрегатом со списком строк заказа. Если вам необходимо выполнить операции над каждым заказом для клиента, используйте orderRepository.GetOrdersForCustomer(customerID); сделайте свои изменения, затем используйте orderRespository.Save(order);

Что касается отслеживания изменений без ORM, вы можете сделать это различными способами, например, агрегат заказов может поднять события, которые прослушивает репозиторий заказов для удаленных строк заказа. Затем они могут быть удалены, когда часть работы завершена. Или немного менее изящный способ состоит в том, чтобы поддерживать удаленные списки, то есть order.DeletedOrderLines, которые, очевидно, может прочитать ваш репозиторий.

Подводя итог:

  • Думаю, вам нужно больше думать о поведении, чем о данных
  • ORM упрощает жизнь для отслеживания изменений, но вы можете сделать это без одного, и вы можете определенно сделать DDD без него.

EDIT в ответ на комментарий:

Я не думаю, что я бы выполнил ленивую загрузку для строк заказа. Какие операции вы, вероятно, будете выполнять на заказ, не требуя строк заказа? Не многие подозревают.

Однако я не должен ограничиваться "правилами" DDD, когда это не имеет смысла, поэтому... Если в маловероятном сценарии есть несколько операций, выполненных по заказу объект, который не требует заполнения строк порядка И, часто существует большое количество строк порядка, связанных с порядком (оба должны были бы быть истинными для меня, чтобы считать это проблемой), тогда я 'd сделать это:

Введите это частное поле в объект заказа:

private Func<Guid, IList<OrderLine>> _lazilyGetOrderLines;

Который будет передан репозиторием порядка в порядке создания:

Order order = new Order(this.GetOrderLines);

Если это частный метод в OrderRepository:

private IList<OrderLine> GetOrderLines(Guid orderId)
{
    //DAL Code here

}

Тогда в свойстве порядка строк может выглядеть так:

public IEnumberable<OrderLine> OrderLines
{ 
    get 
    {
         if (_orderLines == null)
            _orderLines = _lazilyGetOrderLines(this.OrderId);

         return _orderLines;
    }
}

Изменить 2

Я нашел этот пост в блоге, у которого есть аналогичное решение для моего, но немного более элегантное:

http://thinkbeforecoding.com/post/2009/02/07/Lazy-load-and-persistence-ignorance

Ответ 2

1) Должен ли я загружать список заказов в методе GetCustomerById?

Вероятно, хорошая идея отделить код сопоставления заказов от кода сопоставления с клиентом. Если вы пишете свой код доступа к данным вручную, вызов этого модуля отображения из метода GetCustomerById является наилучшим вариантом.

2) Что делать, если сам заказ имеет другое свойство списка и т.д.?

Логика, чтобы собрать всех этих людей, должна где-то жить; связанный с ним совокупный репозиторий является таким же хорошим местом, как и любое другое.

3) Что делать, если я хочу делать ленивую загрузку? Где я могу поместить код для загрузки свойства Orders в клиенте? Внутри свойства Orders get {} accessor? Но тогда мне придется вводить репозиторий в объект домена? который, я думаю, не является правильным решением.

Лучшее решение, которое я видел, - заставить ваш репозиторий возвращать подклассифицированные объекты домена (используя что-то вроде Castle DynamicProxy) - это позволяет вам поддерживать Непосредственность в вашей модели домена.

Ответ 3

Еще один возможный ответ - создать новый объект Proxy, который наследуется от Клиента, называть его ClientProxy и обрабатывать там ленивую нагрузку. Все это псевдокод, поэтому он дает вам представление, а не просто копировать и вставлять его для использования.

Пример:

public class Customer
{
    public id {get; set;}
    public name {get; set;}
    etc...
    public virtual IList<Order> Orders {get; protected set;}
}

вот клиентский класс "прокси"... этот класс не живет в бизнес-слое, а в слое данных вместе с вашими Контекстами и Data Mappers. Обратите внимание, что любые коллекции, которые вы хотите сделать ленивой загрузкой, должны объявляться виртуальными (я считаю, что EF 4.0 также требует, чтобы вы создавали виртуальные реквизиты, как если бы они прокручивали прокси-классы во время выполнения на чистом POCO, чтобы контекст мог отслеживать изменения)/p >

internal sealed class CustomerProxy : Customer
{
   private bool _ordersLoaded = false;
    public override IList<Order> Orders
    {
        get
        {
            IList<Order> orders = new List<Order>();
            if (!_ordersLoaded)
            {
                //assuming you are using mappers to translate entities to db and back
                //mappers also live in the data layer
                CustomerDataMapper mapper = new CustomerDataMapper();
                orders = mapper.GetOrdersByCustomerID(this.ID);
                _ordersLoaded = true;

                // Cache Cases for later use of the instance
                base.Orders = orders;
            }
            else
            {
                orders = base.Orders;
            }
            return orders;
        }
   }
}

Итак, в этом случае наш объект объекта, Клиент по-прежнему свободен от кодовых вызовов базы данных /datamapper, что мы и хотим... "чистые" POCO. Вы делегировали ленивую нагрузку на прокси-объект, который живет на уровне данных, и создает экземпляры данных и совершает вызовы.

есть один недостаток этого подхода, который вызывает код клиента, не может отменить ленивую нагрузку... он либо включен, либо выключен. Так что это зависит от вас в вашем конкретном случае использования. Если вы знаете, что, возможно, в 75% случаев вам всегда понадобятся заказы клиента, то ленивая загрузка, вероятно, не самая лучшая ставка. Было бы лучше, если бы ваш CustomerDataMapper заполнил эту коллекцию в момент получения объекта Customer.

Опять же, я думаю, что NHibernate и EF 4.0 обе позволяют вам изменять ленивые характеристики загрузки во время выполнения, поэтому, как обычно, имеет смысл использовать ORM, b/ca для вас предоставляется множество функций.

Если вы не часто используете Orders, используйте lazy-load для заполнения коллекции Orders.

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

Mike