Внедрение CQRS и Event Sourcing: от разделения чтения и записи до журнала событий

CQRS разделяет модель записи и модель чтения, Event Sourcing хранит не текущее состояние, а ряд фактов, из которых оно собирается. Первое нужно заметно чаще второго, а вместе они нужны реже, чем о них пишут. Мы не продаём шаблон: сначала проверяем, решает ли вашу проблему индекс, представление в базе или таблица истории. Если нет, внедряем по ступеням и так, чтобы вернуться на предыдущую можно было без переписывания.

Обследование, где ответ может быть «не нужно», — от 200 000 ₽Четыре ступени сложности, остановиться можно на любойИсходный код и правила пересборки данных в вашем репозитории

С чем приходят

Какие проблемы приносят под эти названия

Отчёты тормозят основную работу

Тяжёлые выборки по тем же таблицам, что и приём заказов, конкурируют за блокировки. Менеджеры ждут, пока бухгалтер строит свод. Иногда это проблема индексов, иногда разных нагрузок на одни данные.

Нужна история: кто, когда и почему изменил

В таблице лежит текущий статус, а как заявка пришла к нему, видно только по журналу приложения. Аудитор или клиент просит восстановить состояние на дату.

Сложная предметная область с правилами

Склад, страховой полис, договор с этапами: у объекта десятки переходов, и каждый имеет условия. Логика размазана по контроллерам и триггерам.

Систему уже «сделали на CQRS», и она не справляется

Задержка между записью и чтением пугает пользователей, пересобрать данные нельзя, события менялись без версий. Нужен разбор, что оставить, а что упростить.

Ступени

Четыре ступени от простого к сложному: на какой остановиться

Большинство задач решается на первой-второй ступени. Каждая следующая добавляет эксплуатационные расходы, поэтому переходим на неё только по замеру.

Ступень
Когда подходит
Что получаете
Что платите
Ступень1. Одна модель, настроеннаяиндексы, представления
Когда подходитТормозят конкретные отчёты и экраны, данные и права общие
Что получаетеИндексы, переписанные запросы, материализованные представления, реплика базы для отчётов
Что платитеДанные в отчёте отстают на период обновления. Модель остаётся одной
Ступень2. Раздельные модели в одной базекоманды и запросы разведены
Когда подходитЗапись содержит сложные правила, чтение требует других форм данных
Что получаетеКоманды меняют данные через модель с правилами, экраны читают готовые таблицы чтения в той же базе, обновляемые в той же транзакции
Что платитеДве модели вместо одной, больше кода. Задержки нет, пока обе в одной транзакции
Ступень3. Отдельное хранилище чтенияпроекции
Когда подходитЧтение нагружается заметно сильнее записи, нужен полнотекстовый поиск или другая форма хранения
Что получаетеТаблицы чтения строятся по событиям в отдельной базе или поисковом движке, чтение масштабируется независимо
Что платитеИтоговая согласованность: между записью и чтением проходит время. Нужны пересборка проекций и мониторинг отставания
Ступень4. Event Sourcingжурнал фактов как источник истины
Когда подходитНужна полная история объекта, восстановление состояния на любую дату, разбор «как мы сюда пришли»
Что получаетеКаждое изменение хранится как факт, состояние собирается из ряда фактов, проекции строятся из журнала в любой момент
Что платитеВерсии событий, снимки, пересборка, сложные исправления ошибок, другая отладка. Командам без опыта это самая дорогая ступень
Устройство

Как CQRS выглядит в работе, а не на схеме

Схема из двух прямоугольников ничего не объясняет. Разберём путь одной операции, чтобы стало видно, что именно меняется.

Команда и модель записи

Пользователь нажимает «Согласовать заявку». Приложение принимает не «обновить строку», а команду с именем и параметрами. Модель записи проверяет правила: заявка в нужном статусе, у пользователя есть право, лимит не превышен. Если правила нарушены, команда отклоняется с понятной причиной. Если нет, меняется состояние и фиксируется факт, например «заявка согласована».

Модель чтения

Экраны получают данные из таблиц, заточенных под показ: список заявок уже содержит имя заявителя, статус и срок, без пяти соединений. Эти таблицы обновляются либо в той же транзакции (ступень 2), либо отдельным процессом по событиям (ступень 3).

Задержка между записью и чтением

На ступени 3 пользователь нажал «Согласовать», обновил страницу и видит старый статус. Это не ошибка, а свойство схемы, и с ним работают в интерфейсе: после команды показывается результат из ответа на неё, список обновляется по готовности, а при сбое появляется понятное сообщение. Если задержка недопустима в конкретном сценарии, он остаётся на ступени 2.

Что проверяем до внедрения

На выборке реальных запросов определяем, что именно тормозит: запись, чтение или конкуренция за блокировки. Ступень выбираем по замеру. Если хватало индекса и представления, честно говорим, что остальное не нужно.

Event Sourcing

Что даёт журнал событий и чем можно обойтись без него

Основная польза Event Sourcing — в истории и пересборке. Если эти два свойства вам не нужны, цена почти наверняка выше пользы.

Задача
Решение через журнал событий
Как решается без него
Наш вывод
ЗадачаИстория изменений для аудитав таблице только текущий статус
Решение через журнал событийКаждое изменение уже хранится как событие, история — побочный продукт
Как решается без негоТаблица истории, куда триггер или приложение пишет старое и новое значение, плюс журнал действий
Наш выводЕсли нужна история для просмотра и проверки, хватает таблицы истории. Журнал событий оправдан, когда на истории строится логика
ЗадачаСостояние на любую датусколько было на складе вчера
Решение через журнал событийСобирается из событий до нужной даты
Как решается без негоПериодические снимки состояния или таблицы с датами действия записи
Наш выводДля остатков и сумм снимки по расписанию обычно проще
ЗадачаНовый отчёт по старым даннымнужен срез, о котором не думали
Решение через журнал событийПроекция строится заново по всему журналу
Как решается без негоЕсли исходные поля не сохранены, задним числом их уже не получить
Наш выводЭто настоящее преимущество журнала. Оправдано, если срезы часто меняются
ЗадачаИсправление ошибки в данныхвнесли неверное значение
Решение через журнал событийВыпускается событие-исправление, старое не стирается
Как решается без негоПравка записи с пометкой в журнале действий
Наш выводПодходит, где прошлое трогать нельзя. В остальных случаях правка проще
ЗадачаПростая справочная сущностькаталог, контрагенты
Решение через журнал событийЖурнал и проекции для редкоизменяемого справочника
Как решается без негоОбычная таблица
Наш выводНе нужно: затраты без выгоды
Эксплуатация

Что ломается через год: версии, пересборка и персональные данные

Меняются события

Журнал хранится годами, а код меняется. Поле появилось позже, смысл поля поменялся, событие разделили на два. Старые события должны читаться новым кодом: для этого у каждого события есть версия, а при чтении старые версии приводятся к текущей. Без такого механизма журнал через два года превращается в архив, который никто не может прочитать. Правило для всех изменений записывается в каталоге событий, а совместимость проверяется в сборке на сохранённых образцах реальных событий.

Пересборка занимает время

Чем длиннее журнал, тем дольше строится проекция с нуля. Для этого вводят снимки состояния, и пересборка идёт с последнего снимка. Время полной пересборки измеряется и записывается заранее, иначе в день, когда проекция оказалась неверной, выяснится, что исправление займёт сутки при простое. Пересборку проверяем на копии данных, а переключаем проекцию, когда новая готова.

Исправление ошибок в прошлом

Событие, которое уже записано, не меняют. Если оно ошибочно, добавляют исправляющее событие, и проекции учитывают обе записи.

Персональные данные в журнале

Неизменяемый журнал плохо сочетается с требованием удалять данные по запросу. Технически это решается тем, что персональные данные хранятся отдельно, а в событии лежит ссылка, или шифруются ключом субъекта, который можно уничтожить. Какие данные и на каких основаниях удалять, определяет юрист заказчика; мы закладываем техническую возможность. Подробнее о мерах — на странице защиты персональных данных.

Отладка выглядит иначе

Состояние не лежит в одном месте, его нужно собирать. Поэтому в проект входят инструменты: просмотр потока событий по объекту, воспроизведение на копии, сравнение проекции с источником.

Границы

Где CQRS и Event Sourcing не стоит применять

Типовая справочная система

Создали, прочитали, изменили, удалили. Здесь модель записи и модель чтения совпадают, и разделение только удваивает код.

Нет сложных правил и нет требований к истории

Если вся логика сводится к проверке обязательных полей, а история нужна для редкого разбора, достаточно таблицы истории и журнала действий.

Команда без времени на новый подход

Event Sourcing требует дисциплины в версиях, пересборке и инструментах. Если некому это поддерживать, система через год станет сложнее той, которую заменяла.

Задержка недопустима

Экраны, где пользователь сразу после действия должен видеть результат из базы. Такие сценарии остаются на единой модели или на ступени 2.

Что делаем

Из чего состоит работа и сколько она стоит

Цены совпадают со страницей тарифов. Остановиться можно после любой ступени, сделанное остаётся у вас.

Обследование: что тормозит и что нужно хранить

Замеры запросов и блокировок, разбор требований к истории. На выходе рекомендация по ступени (возможно, «добавить индексы») и вилка бюджета. Вычитается из первого этапа.

200 000 ₽ · 2–3 недели

Обследование: несколько систем

Когда журнал или проекции затрагивают учёт, склад и отчётность: схема обменов, источники истины, порядок перехода.

350 000 ₽ · 3–4 недели

Пилот: один агрегат или одна проекция

Один объект (заявка, заказ) переводится на выбранную ступень, рядом со старым путём. Проверка пересборки и задержки на ваших данных.

150 000–400 000 ₽ · 2–8 недель

Пилот: журнал событий для ядра процесса

Event Sourcing для критичного объекта с версиями событий, снимками, инструментами просмотра и пересборки.

400 000–1 200 000 ₽ · 2–3 месяца

Вывод в эксплуатацию

Перенос остальных объектов по одному, регламент пересборки, обучение вашей команды, передача инструкций и тестов.

от 1 500 000 ₽ этапами по 300 000–700 000 ₽ · от 3 месяцев

Сопровождение

Мониторинг отставания проекций, разбор расхождений, плановые доработки с пакета на 60 000 ₽. Сверх пакета 5 000 ₽ в час.

20 000 / 60 000 / 120 000 ₽ в месяц
Процесс

Как идёт работа

2–3 недели — Обследование: замеры и требования

Снимаем журнал медленных запросов, блокировки, профиль нагрузки; отдельно выясняем, какая история и какие срезы нужны бизнесу и аудиту.

Перед разработкой — Выбор ступени и критерии

Записываем, какую ступень выбрали, какую отвергли и по каким замерам. Задаём пороги: допустимая задержка чтения, время пересборки.

От 2 недель — Пилот на одном объекте

Новый путь работает рядом со старым. Сравниваем результаты чтения и записи, проверяем сценарии: сбой обработчика, повтор, ошибочное событие.

Параллельно — Инструменты и пересборка

Просмотр потока событий, сравнение проекции с источником, скрипт полной пересборки с замером времени. Ими пользуется ваша команда.

Перед запуском — Проверка на копии

Полная пересборка на копии боевых данных, проверка версий событий на сохранённых образцах, нагрузочная проверка чтения.

Дальше — Остальные объекты или остановка

Переводим следующий объект, если первый показал пользу. Остановиться можно после пилота.

Цена

Что двигает стоимость внутри вилки

Ступень

Вторая ступень стоит заметно меньше четвёртой. Журнал событий добавляет версии, снимки, инструменты и обучение команды.

Сложность предметной области

Число объектов, переходов и правил. Простая сущность с тремя статусами и объект с двадцатью переходами оцениваются по-разному.

Состояние данных

Если история нигде не сохранялась, прошлое восстановить нельзя. Если данные нужно перенести в журнал, миграция входит в бюджет отдельной строкой.

Требования к задержке и объёму

Допустимое отставание чтения, срок хранения журнала, время пересборки: чем жёстче, тем больше проверок и инструментов.

Контроль

Что остаётся у вас и чего мы не делаем

Остаётся у вас

  • Исходный код в вашем репозитории с первого коммита.
  • Каталог событий с версиями и тесты совместимости на сохранённых образцах.
  • Скрипты пересборки проекций и записанное время полной пересборки.
  • Инструкция по работе с журналом и исправлению ошибок для вашей команды.
  • Право остановиться после любой ступени: сделанное остаётся у вас по акту.

Мы этого не делаем

  • Не внедряем Event Sourcing «на всю систему» сразу: выбираем объекты, где история нужна для логики.
  • Не ведём бухгалтерский и налоговый учёт на журнале событий: сами проводки и отчётность остаются в вашей учётной системе.
  • Не даём юридических заключений о хранении и удалении персональных данных; закладываем технические возможности.
  • Не дежурим круглосуточно: доступность 24/7 в ваших договорах учитывайте отдельно.
  • Не проводим аттестацию, лицензии ФСТЭК у нас нет.
Инструменты

На чём собираем

Хранение и обработка
  • PostgreSQL: таблицы состояния, журнал событий, материализованные представления, реплика для чтения
  • Очередь в PostgreSQL или RabbitMQ для обновления проекций; журнал вроде Kafka только при большом потоке
  • Поисковый движок, если чтению нужен полнотекстовый поиск
  • Python или Go, при необходимости Java по вашему стеку
Контракты и проверка
  • JSON Schema или Protobuf для событий, проверка совместимости версий в CI
  • Тесты пересборки на копии данных
  • Метрики отставания проекций и панель в вашей системе мониторинга
  • Docker, Nginx, GitLab CI, развёртывание в вашем контуре
Материалы

Материалы по теме

Статья

CQRS и Event Sourcing: когда не стоит применять

Пять признаков, что вам это не нужно, и более простые решения: таблица истории, снимки, представления, реплика для отчётов.

  • Признаки, что не нужно
  • Что взять вместо
  • Цена ошибки
Читать →
Статья

Событийная архитектура: что это и когда нужна

Как устроены события, чем событие отличается от вызова и на каких задачах подход окупается.

  • Событие и вызов
  • Когда окупается
  • Признаки, что рано
Читать →
Статья

Микросервисы или монолит: как выбрать архитектуру

Что платите за распределённость и как резать систему по понятиям. Пригодится, если проекции планируется строить в отдельных сервисах.

  • Цена распределённости
  • Границы по понятиям
  • Переход без остановки
Читать →
Заявка

Проверим, нужен ли вам CQRS или журнал событий

Сорок минут созвона: что тормозит, какую историю вы теряете и какие срезы данных нужны бизнесу или аудиту. По итогам скажем, хватит ли индекса или таблицы истории, и если нет, с какой ступени начинать.

Спасибо, заявка отправлена. Ответим в течение рабочего дня.

Не удалось отправить. Напишите в телеграм или позвоните.

Вопросы

Частые вопросы: CQRS и Event Sourcing

Сколько стоит внедрение CQRS и Event Sourcing?

Начинаем с обследования: 200 000 ₽ за один-два процесса, 350 000 ₽ при нескольких системах. Оно вычитается из первого этапа, если продолжаете с нами. Пилот одного объекта 150 000–400 000 ₽, журнал событий для ядра процесса 400 000–1 200 000 ₽. Вывод в эксплуатацию от 1 500 000 ₽ этапами по 300 000–700 000 ₽. Итог зависит от ступени: вторая стоит заметно меньше четвёртой.

Чем CQRS отличается от Event Sourcing?

CQRS разделяет модель записи (команды с правилами) и модель чтения (таблицы под экраны). Event Sourcing хранит изменения как ряд фактов и собирает состояние из них. Первое можно применять без второго, и это встречается чаще. Второе почти всегда идёт вместе с разделением чтения и записи, потому что состояние из журнала читать неудобно.

Можно ли обойтись без Event Sourcing, если нужна история?

Часто да. Таблица истории с полями «что, кто, когда, было, стало», снимки состояния по расписанию и журнал действий закрывают большинство требований аудита. Журнал событий оправдан, когда на истории строится логика или нужно задним числом строить новые срезы.

Что делать, если пользователь видит старые данные после действия?

Показывать результат из ответа на команду, обновлять список по готовности, оставлять сценарий на единой модели, если задержка недопустима. Задержка на третьей ступени неизбежна, это свойство схемы; допустимое отставание задаётся порогом и контролируется мониторингом.

Как исправлять ошибки в журнале событий?

Записанное событие не меняют. Добавляют исправляющее событие, проекции учитывают обе записи. Для ошибок в схеме событий используются версии и приведение старых к текущей. Правила исправления записываются в инструкцию для вашей команды.

Как быть с персональными данными в неизменяемом журнале?

Технически: хранить их отдельно от событий и класть в событие ссылку либо шифровать ключом субъекта, который можно уничтожить. Какие данные и на каких основаниях удалять, решает юрист заказчика; мы закладываем возможность и описываем процедуру.

Можно ли перевести существующую систему постепенно?

Да. Выбираем один объект, строим новый путь рядом со старым, сравниваем результаты, при расхождении откатываемся. Остальные объекты переводим по одному. Остановиться можно после пилота.

Где будет работать система и кому принадлежит код?

На ваших серверах или у выбранного вами хостера. Исходный код, каталог событий и скрипты пересборки лежат в вашем репозитории с первого коммита. Результат работ передаётся вам по акту; порядок передачи прав записывается в договоре; открытые компоненты остаются под своими лицензиями, их список входит в документацию.

Вы работаете только в Ростове-на-Дону?

Мы в Ростове-на-Дону, работаем с заказчиками по всей России: удалённо и в вашем контуре. Доступ к системам и обследование согласуем на старте с вашей службой безопасности.