Транспорт бэкенда
Event-Driven Architecture
Amazon обрабатывает миллионы заказов в день. Когда покупатель нажимает «Купить», срабатывают десятки процессов: проверка оплаты, резервирование на складе, уведомление продавца, расчёт доставки. Как это работает без одного монолитного сервиса, который координирует всё? Через события - каждая система реагирует на факты, а не ждёт команд.
- **Netflix** публикует события просмотра в Kafka. Сервис рекомендаций, аналитики и антифрода подписываются независимо - каждый обрабатывает в своём темпе без coupling.
- **Uber** построил весь Trip lifecycle на событиях: DriverMatched, RideStarted, RideCompleted. 100+ сервисов подписаны на эти события и реагируют самостоятельно.
- **Stripe** хранит все транзакции как immutable события. Это позволяет воспроизводить исторические сценарии, аудировать любое изменение и откатывать состояние на любой момент времени.
Events vs Commands
Command - намерение: "CreateOrder", "CancelPayment". Event - факт, произошедший в прошлом: "OrderCreated", "PaymentCancelled". Эта разница принципиальна: команда может быть отклонена, событие - нет. Команды адресованы конкретному получателю, события - всем, кто хочет слушать.
Amazon и Netflix строят архитектуры на событиях: сервис публикует то, что произошло, не зная кто слушает. Это даёт loose coupling - добавить новый потребитель можно без изменения издателя.
Чем принципиально отличается Event от Command в event-driven архитектуре?
Event Sourcing
Event Sourcing - паттерн хранения состояния как последовательности событий вместо текущего снимка. Текущее состояние = воспроизведение всех событий с начала. Это даёт полную историю изменений, возможность путешествия во времени и аудит по умолчанию.
Event Store хранит события в append-only логе. Для длинных историй используют снапшоты: сохранить текущее состояние на версии 1000, затем воспроизводить только с версии 1001. Kafka при retention=forever становится event store.
Как получить текущее состояние объекта в системе с Event Sourcing?
CQRS: разделение чтения и записи
CQRS (Command Query Responsibility Segregation) разделяет операции записи (Commands) и чтения (Queries) на разные модели. Write-сторона оптимизирована под бизнес-логику и консистентность, Read-сторона - под конкретные запросы UI.
CQRS без Event Sourcing возможен: просто разные модели для чтения и записи в одной БД. Event Sourcing без CQRS тоже возможен. Их часто комбинируют, но это не обязательно.
Какую главную проблему решает CQRS?
Eventual Consistency
В распределённых системах строгая консистентность требует координации между узлами - это медленно и ограничивает доступность (CAP теорема). Eventual Consistency: система гарантирует, что в отсутствие новых записей все узлы в конечном счёте придут к одинаковому состоянию.
Amazon S3 обеспечивает strong consistency для объектов с 2020 года - до этого было eventual. DynamoDB предлагает оба режима. DNS - классический пример eventual consistency: изменение записи расходится по серверам за часы.
Система с eventual consistency показывает устаревший баланс пользователя в течение 200 мс после пополнения. Это:
Event Storming
Event Storming - воркшоп-техника для совместного моделирования бизнес-доменов через события. Введена Альберто Брандолини. Все участники (разработчики + бизнес) клеят стикеры на стену: оранжевые - события, синие - команды, жёлтые - агрегаты.
Event Storming занимает 4-8 часов для сложного домена. Результат - карта событий, из которой видны bounded contexts (границы микросервисов) и критические бизнес-процессы. Используется в Airbnb, ING Bank, Pivotal.
Event-Driven Architecture и Event Sourcing - это одно и то же
EDA - архитектурный стиль коммуникации через события. Event Sourcing - паттерн хранения состояния. Можно использовать EDA без Event Sourcing и наоборот.
EDA описывает как сервисы взаимодействуют (через события вместо прямых вызовов). Event Sourcing описывает как хранить состояние внутри сервиса (лог событий вместо текущего снимка). Это ортогональные концепции.
Какой главный результат сессии Event Storming?
Ключевые идеи
- **Events vs Commands** - событие это факт (нельзя отклонить), команда это намерение (может быть отклонена). Событийная архитектура строится на фактах.
- **Event Sourcing** - состояние как лог событий, текущее состояние = replay. Даёт аудит, историю и возможность построить новые проекции из старых данных.
- **CQRS + Eventual Consistency** - Write модель оптимизирована под консистентность, Read - под UI-запросы. Задержка репликации - осознанный trade-off.
Связанные темы
Event-Driven Architecture опирается на брокеры сообщений и порождает паттерны надёжности:
- Kafka как event log — Kafka - естественный event store для EDA: append-only, retention навсегда, replay из любой точки
- Saga Pattern — Саги - это EDA паттерн для распределённых транзакций: последовательность событий и компенсаций вместо 2PC
Вопросы для размышления
- В каких случаях eventual consistency неприемлема и нужна строгая консистентность? Как это влияет на архитектуру?
- Что происходит, если в Event Sourcing системе нужно изменить схему события v1 на v2, а старые события уже записаны?
- Как Event Storming помогает найти границы микросервисов (bounded contexts) лучше, чем анализ существующего кода?