TGViewer
P(hD)ython P(hD)ython @phdython · 333 subscribers
Post #49 648
«CQRS: нормально делай - нормально будет!»

#system_design

Разбавим Python-посты архитектурой!
На днях со студентами System Design World обсуждали паттерны, и закономерно всплыл CQRS.
Его просто обожают на System Design Interview, и... регулярно путают с CQS!

Micro vs Macro

CQS (Command-Query Separation) - это принцип создания классов и API.

- Command-методы меняют состояние и либо НЕ отдают данные (void), либо возвращают служебные значения (id, ok, error и т.д.);
- Query-методы возвращают данные и никогда НЕ меняют состояние.

На простых классах от CQS мало пользы, зато при написании фабрик, репозиториев и прочих паттернов он реально выручает:
- меньше неявного поведения;
- проще кэшировать и оптимизировать;
- проще поддерживать и дебажить код.

Пример:
from dataclasses import dataclass

@dataclass(frozen=True)
class Car:
num: str

class Base:
def __init__(self):
self._cs = {}

class FactoryBad(Base):
# get with unexpected side effect
def get(self, num: str) -> Car:
if num not in self._cs:
self._cs[num] = Car(num=num)
return self._cs[num]

class FactoryCQS(Base):
def find(self, num: str) -> Car | None:
return self._cs.get(num)

def get(self, num: str) -> Car:
return self._cs[num]

def register(self, num: str) -> None:
if num in self._cs:
raise ValueError(f"Car with number '{num}' already exists!")
self._cs[num] = Car(num=num)


CQRS (Command Query Responsibility Segregation) - это архитектурный паттерн:
- разные пути и модели данных для записи и чтения - write-side и read-side;
- часто разные Handler'ы, контракты и схемы - но всё же это детали реализации.

По сути CQRS - это CQS «на стероидах».

CQRS - Кафка, Стриминг, 2 Сурса

Частая ошибка - воспринимать CQRS как обязательную связку из условных read- и write-СУБД, очереди и Eventual Consistency.
Действительно, так часто бывает, но в первую очередь CQRS - про разделение путей и моделей данных, а не про инфраструктуру.

Начать внедрение CQRS можно и с 1 СУБД:
- write - нормализованные таблицы под базовые сущности;
- read - денормализованные таблицы/view под чтение;
- если обновлять read-проекции синхронно с write-проекциями, можно получить и Strong Consistency.

Зачем всё это

CRUD-сервис «на всё» (create, update, get, find, ...) быстро «пухнет»:
- чтение и запись смешиваются, API становится неочевидным;
- хотим масштабировать чтение, но read-only инстансы вынужденно тащат write-зависимости и флаги/роутинг;
- репозиторий превращается в комбайн с бесконечными зависимостями;
- страдает производительность;
- сложнее растить команду.

CQRS предлагает решение этой проблемы:
- изолированные Handler'ы для команд и запросов (часто реально «по 1 файлу на операцию»);
- лишь нужные зависимости в каждом Handler'е - ускоряет разработку и реально отделяет write-side от read-side.

CQRS - не серебряная пуля:
- если проект компактный и несложный - лучше CRUD + нормальный репозиторий;
- CQRS добавляет бойлерплейт. Даже если код «генерится Claude'ом», растет объём и контекст.

Идемпотентность команд - must have

Команды могут повторяться из-за retry, timeout и at-least-once доставки. Handler должен быть идемпотентным и не допускать создания дубликатов.

Блеск CQRS

CQRS раскрывается, когда система становится read-heavy/нуждается в разных формах данных. Тогда вы:
- масштабируете read- и write-side независимо;
- держите read-модели под конкретные задачи: поиск, отчёты и т.д.

Отдельный плюс - несколько read-моделей одновременно: Postgres для запросов, Elastic для FTS и т.д. При этом они могут строить свои проекции из единого потока событий (event bus, outbox, CDC и т.д.). Отсюда и дружба с Event Sourcing: при хранении изменений как Event Log, проекции можно пересобирать с нуля.

Идеи CQS и CQRS во многом звучат как «нормально делай - нормально будет», они очень интуитивны. Тем не менее выработка общих терминологии и понимания - это всегда большой плюс.

В следующий раз разберём Event Sourcing!

С уважением,
Михаил Масягин

P.S. Рекомендую к просмотру выступление Андрея Цветциха.
  • 🔥 7
  • ❤ 6
  • 🎉 2
More from @phdython
  1. Aug 25, 2026«Конференции и паровозики 🚂» #заметки_с_полей Отгремела «Профсоюзная 2.0» - конфа прошла…
  2. Aug 22, 2026Post #81
  3. Aug 20, 2026«Профсоюз выдвинул 😎» #заметки_с_полей 22 августа в Москве буду спикером на «Профсоюзной…
  4. Aug 16, 2026«Ну вот, я и стал админом 🧑‍💻» #техно_и_хардкор Собрал, казалось бы, банальный, но для м…
  5. Aug 11, 2026«Предзащита: больно и неофициально 🎓» #аспирантские_будни Наконец дошли руки до обещанног…
  6. Jul 18, 2026«Work-life balance от Anthropic ⚒️» #ai4sdlc Недельные лимиты токенов израсходованы - можн…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →