CQS и CQRS

CQS (Command Query Separation) — разделение методов на команды (меняют состояние) и запросы (только читают), на уровне класса/функции. CQRS (Command Query Responsibility Segregation) — то же, но на уровне подсистем/модулей.

CQS — на уровне методов

  • Метод либо меняет состояние и возвращает void, либо читает и возвращает значение. Не оба.
  • Применим к ООП, ФП, процедурному коду — не зависит от парадигмы.
class Counter {
  #n = 0;
  add(x) { this.#n += x; }   // команда: void
  get value { return this.#n; } // запрос: значение
}

CQRS — на уровне подсистем

  • Одна подсистема только пишет, другая только читает.
  • На низком уровне (одна программа) — почти бессмысленно. На уровне распределённой системы — фундамент для масштабирования.
  • Чтений много, запись одна → можно реплицировать чтение.
                       ┌──── Read Model 1 (Postgres + cache)
[Command Bus] → [Write Model] ──events──┤
                       └──── Read Model 2 (ElasticSearch)

Признаки нарушения CQS

// нарушение: возвращает значение И меняет состояние
function pop() { const v = stack[stack.length - 1]; stack.pop(); return v; }

// исправление:
function peek() { return stack[stack.length - 1]; } // запрос
function pop() { stack.pop(); }                    // команда

Когда использовать CQRS

  • Чтений в 100+ раз больше, чем записей.
  • Разные схемы данных для чтения и записи.
  • Event Sourcing — естественно ложится на CQRS.
  • Aggregator/Read Side Projection.

Антипаттерн

// CQRS на маленьком CRUD — overengineering
// Read Model, Write Model, Event Store, Snapshots для приложения с 5 пользователями

🎓 Источники

  • 🎓 [CQS, CQRS, Event Sourcing] · 2019-04-11 · YouTube
    • «CQS — разделение методов на уровне класса. CQRS поднимает разделение до модулей системы».
    • «CQS применим и к ФП, и к ООП — замыкание может нарушать его так же, как класс».
    • На низком уровне CQS почти бессмыслен; CQRS ценен для масштабирования.

См. также