Как стать автором
Обновить

Комментарии 21

Спасибо за статью! Для меня до сих пор не до конца ясен один момент. Если в БД используются автоинкрементные id, а команды не возвращают результат (как везде рекомендуют), то как мне получить результат выполнения с команды в одном http-запросе? Взяв, к примеру, ту же форму добавления комментария? Какие существуют подходы?
Никак. Результат выполнения команды — выполнилась она с ошибкой или без ошибок. А дальше редирект на query. В данном случае на список комментариев.
А если нужно отобразить только что созданный элемент? Хабр, насколько я вижу, не перегружает всё дерево комментариев.
Значит перегрузить дерево комментариев от того коммента на который отвечаем. Ну либо сделать над собой усилие и сгенерировать уникальный идентификатор на клиенте.
Бтв, в рамках паттерна, может так получится, что команда выполнилась, а а ее результат пока в отображение не попадает (например, комментарий не прошел премодерацию) и в этом случае вариант когда вы пытаетесь по Id загрузить комментарий упадет с ошибкой.
Не использовать автоинкрементные id, генерировать их из приложения, например, UUID/GUID. Если нужны целочисленные идентификаторы, посмотрите в сторону Snowflake.
Если вам нужен результат операции, то в CQRS кто-то обязательно должен ждать пока модель на чтение не обновится. Или ждет браузер (несколько http запросов), или ждет сервер (делая такие-же точно запросы к модели на чтение, и удерживая http соединение). По мне, ждать браузером предпочтительнее.
А как найти «свое» обновление в модели среди «чужих»? В любом случае хоть какой-нибудь Id надо либо получить, либо сгенерировать.
Это верно, либо получить, либо сгенерировать самому. Но, например, если вы используете автоинкрементный идентификатор, и получаете его от БД после вставки, то это не совсем чистый CQRS, но, по моему мнению, вполне приемлемо. Вопрос чистоты CQRS тесно связан с производительностью, пока БД выдерживает такие вставки — ну почему нет, на здоровье.
В одном запросе чётко следуя паттерну и генерируя id на стороне сервера никак, а так, навскидку:
1) отображать комментарий без id
2) обновить полностью сущность (например, пост), которая комментируется, вместе со списком комментариев
2.1) запросить дифф между текущим состоянием сущности и известным
3) генерировать id на стороне клиента (guid например)
4) по завершению команды сервер генерирует событие с нужной информацией, а клиент на него подписывается заранее.

Последний вариант наиболее идеологически правилен, в том числе для команд, которые исполняются асинхронно: успешный результат команды означает лишь, что она начала выполняться, или вообще лишь помещена в очередь команд сервера, а результат ещё неизвестно когда будет.
Команда, которая выполняется асинхронно, должна возвращать обещание (Task в C#, promise в js) — а не генерировать события. В противном случае любая необходимость выполнить несколько команд подряд приводит к квесту по жонглированию событиями для программиста.
Это уже дело программиста и экосистемы, как на высоком уровне обернуть HTTP 202 и сообщение по вебсокету о результате.
Никому она ничего не должна. Почитайте про EventSourcing (паттерн который идет рука об руку с CQRS)
Во-первых, CQRS еще не означает использования EventSourcing. Во-вторых, не вижу ничего страшного, неправильного либо нереализуемого в том, чтобы об успешном либо неуспешном добавлении события в основной поток программист смог узнать напрямую, а не через хитрый прослушиватель потока.
Самый первый вариант — отойти от CQS в данном конкретном случае и таки вернуть объект вместе с результатом выполнения команды.
Второй вариант — как предложили выше — генерировать Ids на стороне клиента. В таком случае клиент сможет делать запросы по этому Id с использованием queries
Интересная статья, спасибо.
Правда, не поняла вот этот момент:
Использование доменных классов для транспортировки данных приводит к тому, что все подобъекты (такие как Orders) кастомеров загружаются в память из базы.

LazyLoad нельзя использовать, чтобы избежать этого?
Парадигма DDD оперирует понятием агрегат и корневая сущность агрегата. И грузит из базы агрегат целиком. Это необходимо чтобы вся бизнес логика энкапсулированная внутри классов агрегата применялась верно. Для отображения данных это излишне, собственно отсюда и родился паттерн CQRS.
Понятно, я не в теме )
Буду разбираться дальше, спасибо
LazyLoad нельзя использовать, чтобы избежать этого?

LazyLoad приводит к ошибкам доступа в случае если к коллекции идет обращение когда сессия уже закрыта. Получить на клиенте количество ордеров таким образом не получится
CQRS Тип 0: без CQRS

o_O

А статья хорошая, спасибо.
Самурай без меча подобен самураю с мечом, но только без меча. (с)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории