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

Как Repositories подходят для CQRS?

Согласно Фаулеру (здесь), репозиторий "опосредует между слоями отображения домена и данных, действуя как коллекция объектов домена в памяти". Так, например, в приложении "Курьерская служба" при отправке нового прогона служба моего приложения создает новый объект "Заполнить агрегатный корень", заполняет его значениями из запроса и добавляет их в RunRepository, прежде чем вызывать Группу работы для сохранения изменения в базе данных. Когда пользователь хочет просмотреть список текущих прогонов, я запрашиваю один и тот же репозиторий и возвращаю денормализованный DTO, представляющий информацию.

Однако при просмотре CQRS запрос не попадет в один и тот же репозиторий. Вместо этого он, возможно, будет непосредственно направлен против хранилища данных и всегда будет денормализован. И моя команда будет развиваться в NewRunCommand и Handler, которые будут создавать и заполнять объект домена NewRun, а затем сохраняют информацию в хранилище данных.

Итак, первый вопрос: где репозитории вписываются в модель CQRS, если мы не поддерживаем сборку в памяти (кэш, если хотите) объектов домена?

Рассмотрим случай, когда информация, представленная моей службе приложений, содержит только ряд значений идентификатора, которые служба должна разрешить для создания объекта домена. Например, запрос содержит идентификатор # курьера, назначенного для запуска. Служба должна искать фактический объект Courier на основе значения ID и назначать объект NewRun с использованием метода AssignCourier (который проверяет курьер и выполняет другую бизнес-логику).

Другой вопрос заключается в том, что, учитывая разделение запросов и потенциальное отсутствие репозиториев, как служба приложений выполняет поиск, чтобы найти объект домена Courier?

UPDATE

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

Мне кажется, что CQRS поощряет репозитории, которые являются просто фасадами над механизмами доступа к данным и хранения данных. Они дают "внешний вид" коллекции (как описывает Фаулер), но не управляют сущностями в памяти (как указал Деннис). Это означает, что каждая операция в репозитории является сквозной, да?

Каким образом подразделение труда вписывается в этот подход? Как правило, UoW используется для фиксации изменений, внесенных в репозиторий (правильно?), Но если репозиторий не поддерживает сущности в памяти, то какая роль имеет UoW?

Что касается операции "write", может ли обработчик команды ссылаться на тот же репозиторий, другой репозиторий или, возможно, UoW вместо репозитория?

4b9b3361

Ответ 1

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

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

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

Это может помочь увидеть пример использования этих подходов. Возможно, посмотрите на следующие проекты:

В случае с NES репозиторий просто предоставляет знакомый интерфейс для добавления и чтения агрегатов непосредственно в единицу работы.

Еще несколько ссылок, которые могут помочь:

Ответ 2

Я не уверен, насколько это ортодоксально, но в текущем проекте у меня есть репозиторий для моего общего сущности сущности. Этот репозиторий имеет только два метода: Get и ApplyEvents.

Все события реализуют общий интерфейс для своего типа - для заказов есть OrderEvents и т.д. Я лично ставил бизнес-логику каждого события в полиморфный метод, так что добавление новых типов событий становится очень простым.

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

ApplyEvents принимает список событий, а затем изменяет состояние объекта на основе этих данных и возвращает его. Обратите внимание, что вы предоставляете репозиторию возможность воссоздать объект, а не просто его изменять! Это хорошо работает с функциональным типом программирования, но лучше всего избегать равенства объектов (obj1 == obj2) в С# или Java. Я бы сказал, что только ValueObjects, а не Entities, всегда должны иметь равенство.

Здесь, как это работает на практике (С#) - у меня есть Orders, и я хочу добавить элемент. currentOrder.Items возвращает пустой список. Тогда я делаю

Assert.IsFalse(newEvent.Items.Any())
IOrderEvent newEvent = eventFactory.CreateOrderItemEvent(myItemID);
currentOrder = orderRepository.ApplyEvents(currentOrder, newEvent);
Assert.IsTrue(newEvent.Items.Any())

Теперь я должен увидеть currentOrder.Items есть одна запись.

Недостатки здесь в том, что вся моя обработка выполняется через события, а не с моей бизнес-логикой в ​​Entity. Однако в моем случае, когда почти все мои объекты должны быть сериализуемыми (в основном POCOs) и работать с несколькими системами, это действительно хорошо работает.