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 ценен для масштабирования.