CQRS — это про разные модели для записи и чтения.
CQRS — не про то что возвращает command bus. Ты можешь иметь обычные FooService->bar(), там будет обращение к write model и всё.
Разделение на write/read models обычно элементарное и напрашивается само собой. Ну, например, полю «age»
как правило нечего делать в сущности User, потому что это вычисляемое значение. Соответственно, не должно быть команд, которые меняют возраст: это бессмыслица. Различных вычисляемых значений может быть достаточно много и не все они будут вообще похожи на исходную сущность. Далее нужно как-то гарантировать доставку изменений до read models, некоторые можно сохранять в той же транзакции (чего уж там), для некоторых сделать (полу-) event sourcing с гарантированной доставкой через какую-нибудь Kafka, можно еще заюзать какой-нибудь Debezium, но это всё технические детали.
CQRS в этом смысле прост: огромное количество моделей выглядят по-разному при записи и чтении. write-model — пачка методов, возвращающих void. read-model — обычная DTO с геттерами, но и она может иметь некоторую логику для вывода.
write-model может при релизе своих событий возвращать эти самые события, в некоторых случаях их достаточно для ответа (тот же id-шник, результат действия в игре «Морской бой» [попал/не попал/ранил/утопил/победил] и т.д. и т.п., каждый раз возвращать всё игровое поле необязательно — клиент не может запомнить какие поля уже отыграли?).
Но можно и read-model возвращать в API, но тогда зачастую нужно гарантировать ее актуальность, сделать синхронный запрос, никаких противоречий с CQRS тут нет. Можно на тот же GraphQL посмотреть — там из мутаций можно вернуть какую угодно read model (если в схеме это укажешь).
@AmdY же рассказывает, что он сделал запрос, а потом опрашивал сервер на предмет его успешности.
Когда
@AmdY сделал первый запрос на изменение, он зафиксировал транзакцию (иначе это просто будет через раз работать). Он выполнил вполне себе команду. Просто она может называться как-то вроде «RequestSmth» или классический пример с PlaceOrder (в его примере команда может называться «отчитаться при выходе из шахты», «покинуть шахту» или еще миллионом способов; и это не предполагает, что эта команда будет синхронно высчитывать какую-то статистику — с чего бы? Мы фиксируем факт завершения работы шахтера и всё). Мы размещаем по сути заявку на заказ, но там еще будет череда проверок и промежуточных транзакций (бронирование со склада и прочее).
Это называется long-living transaction и это решается через те же saga, process manager и прочие.
И никакого отношения к CQRS это не имеет.
Команда должна быть простой. Команда вносит атомарное изменение. Атомарное изменение должно быть тривиальным и, как можно догадаться, оно не должно затрагивать несколько серверов.
P.S. UUID на стороне клиента может еще служить ключом идемпотентности. Но и это не имеет никакого отношения к CQRS.