Event-driven vs Request-driven: почему микросервисы превращаются в спагетти

Misha_Backend
2026-08-03 08:51
Заметил тренд: все кинулись пихать Kafka и RabbitMQ куда ни попадя, даже там, где обычный REST-вызов решает задачу проще и надёжнее. В итоге получаем распределённый монолит на событиях, где трассировка запроса превращается в квест с детективом, а дебаг — в археологические раскопки. Давайте разберёмся на пальцах. Request-driven — это когда сервис A напрямую дёргает сервис B по HTTP/gRPC и ждёт ответ. Просто, предсказуемо, легко тестировать. Минусы — синхронная связность, каскадные фейлы, и если B лёг — A тоже лежит. Event-driven — это когда A шлёт событие в брокер, а B, C и D подписываются и делают что хотят. Асинхронно, слабо связано, но появляется ад с идемпотентностью, порядком сообщений и eventual consistency. Типичная ошибка новичка: они видят слово «масштабирование» и сразу пишут топик на каждое чихание. Пример из практики: у нас был сервис заказов, который после создания заказа публиковал 5 разных событий — order_created, payment_started, inventory_reserved, notification_sent, audit_logged. В итоге чтобы понять, что заказ вообще создан, нужно было собирать пазл из пяти топиков и трёх consumer-групп. А ведь достаточно было одного REST-вызова к inventory и fire-and-forget событию для аудита. Правило, которое я вывел для себя за годы: если событие влияет на бизнес-процесс и требует гарантированного выполнения — это request. Если это уведомление, аналитика или побочный эффект — event. Классика: оплата — request (нужен ответ, иначе юзер занервничает), отправка email-уведомления — event (можно потерять, никто не умрёт). Ещё одна боль — саги. Все любят саги, пока не приходится дебажить компенсацию на третьем шаге, когда payment уже списал, а stock не освободился. Я видел саги на 15 шагов с ручными retry и DLQ, которые в проде превращаются в тикающий бомбовый механизм. Мой совет: сначала сделайте request-driven версию с явными таймаутами и ретраями, потом если реально упрётесь в нагрузку — рефакторите на события. И последний пункт — observability. В request-driven мире у тебя есть correlation_id и ты видишь весь путь запроса в Jaeger. В event-driven — каждый consumer имеет свой span, и чтобы связать их, нужно тащить trace_id через заголовки событий, что половина команд забывает. Итог — в Grafana ты видишь, что consumer упал, но почему и из-за какого события — хрен поймёшь. К чему я это всё: не гонитесь за модой. Архитектура должна решать бизнес-задачи, а не тешить ЧСВ архитектора. Если ваш проект — типичный CRUD на 5 сервисов, события вам нужны только для аудита и уведомлений. А если уж зашли на event-driven — закладывайте идемпотентность, саги и трассировку с первого дня, иначе потом перепишете всё с нуля.
👍 2
Виталик_Электроник
2026-08-03 09:25
Слушай, ну ты в самую точку попал про «распределённый монолит» — это бич всех, кто начинает с малого, а потом обрастает событиями как снежный ком! 😅 У меня самого была история: делал метеостанцию на ESP32, и решил «по-взрослому» — поднял локальный MQTT-брокер, чтобы датчики публиковали события, а контроллеры на них подписывались. В итоге, чтобы узнать температуру, мне пришлось прокидывать три топика и два уровня ретрансляции, а когда один узел потерял соединение — всё остальное встало колом. Потом взял и тупо переписал на прямые HTTP-запросы с таймаутом — и проблема ушла, а код стал читаемым. Так что твоё правило «request для гарантий, event для уведомлений» — это прям база, которую я теперь всегда вбиваю в голову своим студентам на курсах. Но вот что ещё добавлю: даже в event-driven мире можно жить спокойно, если не пытаться эмулировать синхронность через брокер. У нас на работе была сага на 8 шагов с компенсациями, и знаешь, что спасло? Не магия, а простой чек-лист: идемпотентные ключи на каждое событие, явный порядок через partition key, и отдельный сервис-«дирижёр», который не лез в бизнес-логику, а просто следил за статусами. И да — мы с первого дня зашили trace_id в заголовки всех сообщений, иначе бы мы сами потонули в своих же топиках. Так что если уж зашёл на события — сначала наведи порядок с observability, потом уже строй распределённые пляски. А иначе лучше честно оставаться на REST, пока проект не вырос до реальных проблем с масштабированием. 🔧⚡
🚫 1
Сергей_Нуб
2026-08-03 09:49
Да, бро, ты прямо в точку про «распределённый монолит»! 😄 У меня тоже была шиза: начал пихать RabbitMQ в пет-проект, потому что «ну как же без событий». В итоге три топика ради того, чтобы юзер получил уведомление о регистрации, а сам регистрация — обычный POST. Переписал на REST + fire-and-forget для письма — и всё стало ясно как день. Но добавлю ещё один грабли: многие забывают, что event-driven — это не про «все асинхронно», а про «слабое связывание и гарантии». Если у тебя нет идемпотентности и нормального trace_id — ты просто копаешь себе яму. Я бы вообще советовал новичкам начинать с request-driven и добавлять события только тогда, когда реально больно от синхронных вызовов. А саги — это вообще отдельный вид боли, лучше сначала на бумаге нарисовать все шаги и компенсации, а потом код писать. Иначе получится как у меня — половину ночи дебажил, почему компенсация не сработала из-за кривого partition key. 😅

Войдите или зарегистрируйтесь, чтобы ответить.