CQRS и MediatR

↑ 8.2 Архитектурные стили и DDD · ← Предыдущая · → Следующая

Обязательно~3 мин чтенияУровень: senior

Зачем это на собесе

CQRS упоминают везде; нужно различать CQRS как принцип и «тяжёлый» вариант с Event Sourcing и отдельными хранилищами.

Объяснение

CQRS (Command Query Responsibility Segregation) — разделение модели записи (команды меняют состояние) и модели чтения (запросы возвращают данные).

УровеньЧтоКогда
Простойотдельные обработчики команд и запросов, одна БДпочти всегда полезен
Раздельные моделизапись — доменная модель, чтение — проекции/DTO (Dapper, view)разные требования к чтению и записи
Раздельные хранилищачтение из денормализованного хранилища (реплика, ClickHouse, Elasticsearch), синхронизация событиямивысокая нагрузка на чтение, eventual consistency
CQRS + Event Sourcingзапись — поток событийособые требования к аудиту
public record CreateOrder(Guid CustomerId, List<ItemDto> Items) : IRequest<Guid>;          // команда
public record GetOrder(Guid Id) : IRequest<OrderDto?>;                                       // запрос
 
public class CreateOrderHandler(AppDbContext db) : IRequestHandler<CreateOrder, Guid> { /* меняет состояние */ }
public class GetOrderHandler(IDbConnection conn) : IRequestHandler<GetOrder, OrderDto?> { /* читает Dapper-ом */ }
 
builder.Services.AddMediatR(c => c.RegisterServicesFromAssemblyContaining<Program>());
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));   // сквозная валидация
 
var id = await mediator.Send(new CreateOrder(customerId, items), ct);

MediatR — реализация паттерна Mediator: Send для запросов/команд, Publish для уведомлений, IPipelineBehavior для сквозных задач (валидация, логирование, транзакция). Альтернативы: Wolverine, свой мини-диспетчер, прямые вызовы обработчиков (Vertical Slice).

Нюансы и подводные камни

  • MediatR — не обязательная часть CQRS; в новых версиях лицензия изменилась (коммерческая для организаций), рассмотрите альтернативы.
  • Разные хранилища читателей приводят к eventual consistency: клиент может не видеть свою запись сразу.
  • Команда не должна возвращать данные для отображения (максимум идентификатор).
  • Скрытая «магия» Mediator усложняет навигацию по коду.
  • CQRS для простого CRUD — избыточен.

Практика

  1. Разделите один сервис на команды и запросы с MediatR.
  2. Добавьте pipeline behavior валидации и логирования.
  3. Реализуйте read-модель списка заказов через Dapper.

Вопросы с ответами

Связанные темы