Что такое CQRS и зачем он нужен?
Что проверяют
Отличаете ли вы сам паттерн от того, что к нему часто прикручивают: отдельные базы, шину сообщений, Event Sourcing. Следом просят показать, как это выглядит в .NET-проекте, и спрашивают, где CQRS только мешает.
Как отвечать
CQRS (Command Query Responsibility Segregation) разводит запись и чтение по разным моделям. Корень у него в принципе CQS Бертрана Мейера: метод либо меняет состояние, либо возвращает данные. Грег Янг поднял эту идею с уровня метода на уровень архитектуры.
- команда: намерение изменить систему, например
record CreateOrder(Guid CustomerId, ...). Проходит валидацию, работает с доменной моделью и возвращает минимум, обычно идентификатор; - запрос: только чтение без побочных эффектов. Возвращает готовый DTO под конкретный экран;
- обработчики: у каждой команды и запроса свой класс, скажем
ICommandHandler<TCommand>. Долго их связывали через MediatR, но с 2025 года его новые версии идут под коммерческой лицензией, поэтому всё чаще встречаются Wolverine, генераторMediatorили обработчики, просто внедрённые через DI.
Типичная реализация на одной базе: команды идут через EF Core с отслеживанием изменений и доменными правилами, запросы читают через AsNoTracking() с проекцией в DTO или через Dapper. Уже это даёт главную пользу: модель записи не уродуется под нужды отчётов.
Следующая ступень: отдельное хранилище для чтения (реплика, денормализованные таблицы, Elasticsearch), которое обновляется по событиям. Здесь появляется согласованность в конечном счёте: заказ создан, а в списке его ещё нет. Это надо проговорить сразу как цену решения.
Применять стоит там, где чтение и запись заметно расходятся по нагрузке или форме данных. Для CRUD-админки это лишний слой.
Где спотыкаются
Отождествляют CQRS с Event Sourcing. Их часто используют вместе, но CQRS прекрасно живёт на одной базе без журнала событий.
Считают две базы обязательными и не могут объяснить, как пользователь увидит только что записанные им же данные.
Сводят CQRS к «у нас есть MediatR». Библиотека лишь диспетчеризует вызовы, и проект, где «запросы» через медиатор меняют данные, CQRS не является.