Задний план
Уди Дахан предлагает стратегию получения в качестве полезного шаблона для использования. для доступа к данным. Я согласен.
Идея состоит в том, чтобы сделать роли явными. Например, у меня есть Совокупный корень — Клиент. Я хочу, чтобы клиент был в нескольких частях моего приложения: список клиентов для выбора, просмотр сведений о клиенте и кнопка для деактивации клиента.
Кажется, Уди предложил бы интерфейс для каждой из этих ролей. Итак, у меня есть ICustomerInList
с очень простой информацией, ICustomerDetail
, которая включает последние 10 приобретенных продуктов, и IDeactivateCustomer
, в которой есть метод деактивации клиента. Каждый интерфейс предоставляет ровно столько корневой совокупности клиентов, сколько необходимо для выполнения работы в каждой ситуации. My Customer Aggregate Root реализует все эти интерфейсы.
Теперь я хочу реализовать стратегию получения для каждой из этих ролей. Каждая стратегия может загружать разное количество данных в мой совокупный корень, потому что он будет находиться за интерфейсом, предоставляющим только необходимые биты информации.
Общий метод реализации этой части — запросить Service Locator или какой-либо другой стиль внедрения зависимостей. Этот код возьмет нужный вам интерфейс, например ICustomerInList
, и найдет стратегию загрузки для его загрузки (IStrategyForFetching<ICustomerInList>
). Эта стратегия реализуется классом, который знает, что нужно загружать в Customer только биты информации, необходимые для интерфейса ICustomerInList.
Все идет нормально.
Вопрос
То, что вы передаете в Service Locator, или файл IStrategyForFetching<ICustomerInList>
. Все примеры, которые я вижу, выбирают только один объект по известному идентификатору. Этот случай прост, вызывающий код передает этот идентификатор и возвращает конкретный интерфейс.
А если я хочу поискать? Или я хочу страницу 2 списка клиентов? Теперь я хочу передать больше терминов, необходимых для стратегии выборки.
Возможные решения
В некоторых примерах, которые я видел, используется предикат — выражение, которое возвращает true или false, если конкретный совокупный корень должен быть частью результирующего набора. Это хорошо работает для условий, но как насчет того, чтобы вернуть первых n клиентов и не более? Или получение страницы 2 результатов поиска? Или как сортируются результаты?
Моя первая реакция — начать добавлять общие параметры к моему IStrategyForFetching<ICustomerInList>
. Теперь он становится IStrategyForFetching<TAggregateRoot, TStrategyForSelecting, TStrategyForOrdering>
. Это быстро становится сложным и уродливым. Это еще больше усложняется разными репозиториями. Некоторые репозитории предоставляют данные только при использовании определенной стратегии выбора, некоторые только определенные типы упорядочения. Я хотел бы иметь гибкость для реализации общих репозиториев, которые могут принимать функции сортировки вместе со специализированными репозиториями, которые возвращают только совокупные корни, отсортированные определенным образом.
Похоже, я должен применить тот же шаблон, который использовался в начале — как сделать роли явными? Должен ли я реализовать стратегию получения X (Agggregate Root) с использованием полезной нагрузки Y (параметры поиска/упорядочивания)?
Изменить (2012-03-05)
Все это по-прежнему действует, если я не возвращаю совокупный корень каждый раз. Если каждый интерфейс реализуется другим DTO, я все равно могу использовать IStrategyForFetching. Вот почему этот паттерн является мощным — то, что делает выборка и что возвращается, не должно каким-либо образом сопоставляться с корнем агрегата.
В итоге я использовал IStrategyForFetching<TEntity, TSpecification>
. TEntity — это то, что я хочу получить, TSpecification — это то, как я хочу это получить.