Реализация стратегии извлечения Udi - Как мне искать? - PullRequest
3 голосов
/ 29 февраля 2012

Фон

Уди Дахан предлагает стратегию извлечения в качестве полезного шаблона для доступа к данным. Я согласен.

Концепция состоит в том, чтобы сделать роли явными. Например, у меня есть Совокупный корень - Клиент. Я хочу, чтобы клиент находился в нескольких частях моего приложения - список клиентов на выбор, просмотр сведений о клиенте, и я хочу кнопку, чтобы деактивировать клиента.

Кажется, Уди предложил бы интерфейс для каждой из этих ролей. Таким образом, у меня есть 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 - это то, как я хочу получить.

Ответы [ 2 ]

3 голосов
/ 02 марта 2012

Вы сталкивались с CQRS ?Udi является большим сторонником этого , и его цель - решить именно эту проблему.

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

Краткая последовательность того, как это работает:

  • На экране отображается список клиентов, которые имеютсделал заказы на прошлой неделе.
  • Пользовательский интерфейс вызывает CustomerQueryService, передавая в качестве критерия дату.
  • CustomerQueryService выполняет запрос, который возвращает только поля, необходимые для этого экрана, включая совокупный идентификатор каждого клиента.
  • Пользователь выбирает клиента в списке и выполняет действие «Сделать важного клиента» /command.
  • Пользовательский интерфейс отправляет команду MakeImportantCommand в службу команд (или службу приложений в терминах DDD), содержащую идентификаторклиента.
  • Служба команд извлекает агрегат Customer из хранилища, используя ID, переданный в команде, вызывает необходимые методы и обновляет базу данных.

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

Я знаю, что ваш вопрос касался «извлечения стратегии», но я заметил, что он написал эту статью в 2007 году, и, вероятно,что он считает CQRS своим наследником.

Подводя итог моему ответу:

  1. Не пытайтесь спроектировать и сократить DTO от ваших доменных агрегатов.Вместо этого просто создайте отдельные службы запросов, которые предоставят вам индивидуальный запрос для ваших нужд.
  2. Ознакомьтесь с CQRS (если вы этого еще не сделали).
1 голос
/ 03 марта 2012

Чтобы добавить к ответу Дэвид Мастерс , я думаю, что все интерфейсы стратегии извлечения добавляют ненужную сложность. Наличие AR-клиента, реализующего различные интерфейсы, которые моделируются после пользовательского интерфейса, является ненужным ограничением для класса AR, и вы приложите немало усилий, чтобы реализовать его. Более того, это хрупкое решение. Что если представлению требуются данные, которые хотя и относятся к Customer, но не принадлежат к классу customer? Приводит ли затем к классу клиента и соответствующим сопоставлениям ORM эти данные? Почему бы просто не иметь отдельный набор классов для целей запроса и покончить с этим? Это позволяет вам выбирать стратегии в том месте, где они принадлежат, - в хранилище. Кроме того, какое значение действительно добавляет абстракция интерфейса стратегии выборки? Это может быть подходящей моделью того, что происходит в приложении, но это не помогает в ее реализации.

...