2009-07-17 4 views
10

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

Источником моей путаницы является DTO против DDD и репозиториев. Я хочу, чтобы у моих объектов домена POCO были умны, и я хочу получить их из репозиториев. Но мне кажется, что я должен нарушать некоторые правила инкапсуляции, чтобы сделать эту работу, и похоже, что она может превратить DTO в их головы.

Вот простой пример: в нашем каталоге приложение может быть пакетом, который включает в себя ряд других частей. Таким образом, для Part POCO имеет смысл использовать метод GetChildren(), который возвращает IEnumerable < Part>. Это может даже сделать другие вещи со списком на своем пути.

Но как этот список разрешен? Кажется, что хранилище является ответом:

interface IPartRepository : IRepository<Part> 
{ 
    // Part LoadByID(int id); comes from IRepository<Part> 
    IEnumerable<Part> GetChildren(Part part); 
} 

И

class Part 
{ 
    ... 
    public IEnumerable<Part> GetChildren() 
    { 
     // Might manipulate this list on the way out! 
     return partRepository.GetChildren(this); 
    } 
} 

Так что теперь потребители моего каталога, в дополнение к (правильно) загрузки деталей из хранилища, может также обойти некоторые части капсулированных логики, напрямую вызывая GetChildren (часть). Разве это не так плохо?

Я читал, что хранилища должны обслуживать POCO, но DTO хороши для передачи данных «между слоями». Вычисляются многие свойства деталей - цены, например, рассчитываются на основе сложных правил ценообразования. Цена даже не будет в DTO, поступающей из репозитория, поэтому, похоже, передача данных о ценах обратно в веб-службу требует, чтобы DTO потреблял часть, а не наоборот.

Это уже слишком длинное. Где моя голова отвинчена?

ответ

2

Один из подходов, который решает эту проблему, состоит в том, чтобы переместить логику в сами дочерние части, т. Е. Изменить семантику класса, чтобы объекты, отвечающие за преобразование себя, когда они связаны с родительским объектом, отвечают за объекты Part.

Например, если цена Part зависит от его родительского Part, цена может быть определена в следующих случаях (как минимум):

  • по строительству, если родитель Part (и все другие необходимые данные).

  • В способе AttachToParent(Part parentPart) или в ответ на событие, то есть OnAttachedToParent(Part parentPart).

  • Когда это необходимо по коду клиента (то есть при первом доступе его имущества Price).


Edit: мой первоначальный ответ (ниже) на самом деле был не в духе DDD. Это включало создание простых объектов объектов домена, которые многие считают анти-шаблоном (см. Anemic Domain Model).

Дополнительный слой между Part и IPartRepository (я буду называть его IPartService) решает эту проблему: перемещение GetChildren(part) в IPartService и удалить его из Part, а затем сделать клиентский код вызова IPartService, чтобы получить Part объекты и их дети, а не попав непосредственно в хранилище. Класс Part по-прежнему имеет свойство ChildParts (или Children) - он просто не знает, как его заполнить.

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

+0

Интересно. Но меня смущает «переместите GetChildren (часть) в IPartService и удалите его из части», а затем «Класс Part все еще имеет свойство Childparts». Что, если часть нуждается в массаже своих детей по какой-то причине? – n8wrl

+1

Если массирование должно произойти сразу же после извлечения из репозитория, я бы поставил эту логику в 'IPartService.GetChildren()'. Если вам нужно иметь возможность изменять дочерние части в произвольные моменты времени, вы можете создать другой метод службы, например «IPartService.UpdateChildPartPrices (часть)». (Или оба - вы можете вызывать «UpdateChildPartPrices» из «GetChildren».) –

+0

Спасибо, что нашли время, Джефф! – n8wrl

0

Недопустимая часть уравнения здесь - это поведение объекта Parts и как вы хотите работать с агрегатом. Вам нужно работать с отдельными детьми каждого из Part с n-й рекурсией или работать с «корнем» Part (т.е. без родителей) и детьми в целом?

Имея Part совокупный корень, который содержит список довольно обобщенно типизированных Parts как дети, кажется, что это не выразить свою модель предметной области, особенно хорошо, но вы могли бы сделать это и рекурсивно ленивым нагрузки каждую коллекцию детской. Тем не менее, я все равно буду очень осторожен, когда возможна бесконечная рекурсия.

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

Они очень полезны, если вы используете сервис-ориентированную архитектуру (вы упоминаете веб-службы, но это может быть любая SOA). Ваши службы будут запрашивать ваши репозитории, выполнять какую-либо дополнительную работу, а затем сопоставлять объекты вашего домена в плоские DTO для отправки обратно запрашивающим клиентам. DTO должны быть простыми, не содержать никакой логической и прикладной функции, специфичной с целью сериализации.

Используйте свои объекты домена внутри приложения и внешние DTO.

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