Монолит не умер. Вы просто плохо его написали
В 2015 году все разбивали монолиты на микросервисы. В 2020-м начали писать о "modular monolith". Сейчас – снова пишут про "return to monolith". Маятник качается, а суть не меняется: архитектура должна соответствовать задаче, а не тренду.
Что не так с микросервисами по умолчанию
Микросервисы решают реальные проблемы: независимый деплой, масштабирование отдельных компонентов, изоляция отказов, независимые команды. Но всё это имеет цену, которую часто недооценивают на старте.
- Сетевые вызовы вместо функций – latency, retry, circuit breaker
- Распределённые транзакции – saga pattern вместо BEGIN/COMMIT
- Observability с нуля – трейсинг между сервисами, distributed logging
- Отладка – воспроизвести баг локально становится задачей на полдня
- Версионирование API между сервисами – backward compatibility навсегда
Для команды из 3-5 человек на раннем продукте – это всё overhead, который замедляет разработку в несколько раз.
Хорошо написанный монолит
Проблема не в монолите как архитектуре – проблема в монолите без внутренних границ. "Big ball of mud" – это не монолит, это отсутствие архитектуры.
Modular monolith – монолит с чёткими внутренними модулями, каждый из которых имеет явный публичный API. Модули не обращаются к базе данных друг друга напрямую. Зависимости – только через интерфейсы.
// Плохо – модуль Orders знает про таблицу users const order = await db.query(` SELECT o.*, u.email FROM orders o JOIN users u ON u.id = o.user_id WHERE o.id = $1 `, [orderId]); // Хорошо – Orders вызывает UsersModule через интерфейс const user = await usersModule.getUserById(order.userId); const order = await ordersModule.getOrder(orderId); Признаки, что пора делить
Монолит становится проблемой в конкретных ситуациях:
// разные требования к масштабированию
Если один компонент (например, генерация отчётов) потребляет в 50 раз больше ресурсов, чем остальные – имеет смысл вынести его отдельно. Не весь монолит, а конкретный bottleneck.
// независимые команды
Если над разными частями продукта работают отдельные команды с разным release cadence – монолит создаёт coordination overhead. Но это проблема возникает при 15+ инженерах, не при 5.
// разные требования к надёжности
Платёжный сервис и сервис уведомлений – разные SLA, разные требования к безопасности, разные on-call процедуры. Изоляция оправдана.
Стратегия
Начните с монолита. Пишите его модульно – чёткие границы, никаких "заглядываний" в чужие данные. Когда появится конкретная боль (не теоретическая, а настоящая) – вырежьте сервис по уже готовой модульной границе. Это займёт неделю, а не месяц.
Если вы не можете написать хороший монолит, вы не напишете хорошие микросервисы. Вы просто распределите плохой монолит по сети.