В прошлом уроке ksqlDB собирал таблицы из потоков событий. Сегодня перевернём привычную картину ещё сильнее. Обычно главное у сервиса это база, а события в Kafka просто уведомления о том, что в базе что-то поменялось. Но можно сделать наоборот: главными станут события, а таблицы мы будем собирать из них, когда понадобятся. У «Гонца» так устроено табло Отто, и сегодня разберём, почему это удобно.
Банковская выписка
Открой выписку по карте. В ней нет строки «остаток», которую банк переписывает после каждой покупки. Там список операций: зарплата пришла, кофе купил, за связь заплатил. Остаток банк считает из этого списка, сложив все операции с начала.
У такой выписки три полезных свойства. Ничего не теряется: видно, откуда взялся каждый рубль. Остаток можно пересчитать в любой момент, хоть на вчерашнее утро. А если завтра банк захочет показать «траты на еду за месяц», он просто пройдёт по тем же операциям ещё раз и посчитает другое.
Банк хранит операции, а остаток для него производная. Так устроен event sourcing.
Хранить факты, а не состояние
Event sourcing означает: храним не текущее состояние, а все события, которые к нему привели. Заказ order-41 в таблице выглядел бы одной строкой со статусом «доставлен». В журнале событий он выглядит так:
оффсет ключ событие
0 order-41 created 690
2 order-41 courier-assigned 15
3 order-41 picked-up
5 order-41 delivered
Чтобы узнать, где сейчас заказ, события проходят по порядку и применяют одно за другим: создан на 690 рублей, назначен курьер 15, забран, доставлен. Такое последовательное применение называют свёрткой. Каждое событие меняет только то, о чём сообщает: courier-assigned добавляет курьера, а сумму оставляет прежней.
Kafka для такого журнала подходит идеально. Мы видели в уроке про лог, что записи в партиции только дописываются, у каждой свой оффсет, а порядок событий одного ключа не меняется, как мы разбирали в уроке про ключи. Ключ здесь это номер заказа, поэтому история заказа лежит в одной партиции целиком и читается по порядку.
Запись отдельно, чтение отдельно
Табло Отто, отчёт о выручке и экран клиента «где мой заказ» хотят от одних и тех же событий разного. Табло нужен текущий статус каждого заказа. Отчёту сумма доставленных заказов за день. Клиенту история одного заказа с временем каждого шага.
Строить для них одну общую таблицу неудобно: что подходит табло, неудобно отчёту. Поэтому запись и чтение разделяют. Это разделение называют CQRS. Пишут события в одно место, в топик Kafka. А для чтения каждый строит свою модель для чтения, проекцию: отдельный консьюмер со своей группой читает события и складывает их так, как удобно ему.
┌─ группа board → табло статусов
сервис заказов → топик ──┼─ группа revenue → выручка за день
order-events └─ группа history → история заказа для клиента
Каждая проекция живёт сама по себе и ничего не знает о соседях. Новая проекция появляется без единой правки в сервисе заказов: её консьюмер просто читает топик с начала.
урок из курса
Урок из курса «Data Engineering». Курс покупается один раз и остаётся навсегда: 500 ₽.
Оплата пока не подключена: премиум и курсы открываются промокодом.