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