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

Ответ

В моем опыте с Node.js проектами, решение о выборе архитектуры принималось коллегиально, но с четким распределением ответственности:

Проект #1: Стартап в области EdTech

  • Решение: Монолит на Express.js
  • Кто принимал: Основатель + я (единственный backend-разработчик)
  • Причины:
    • Быстрый вывод MVP на рынок (2 месяца)
    • Команда из 3 человек
    • Простота деплоя на Heroku
    • Единая кодовая база для всего функционала
// Структура монолита
project/
├── src/
│   ├── controllers/    # UserController, CourseController
│   ├── models/         # Mongoose схемы
│   ├── routes/         # API endpoints
│   ├── middleware/     # Auth, validation
│   └── utils/          # Вспомогательные функции
├── package.json
└── server.js          # Единая точка входа

Проект #2: Финансовая платформа с 50k+ пользователей

  • Решение: Микросервисы на Node.js
  • Кто принимал: Технический директор + архитектор + команда тимлидов
  • Причины:
    • Необходимость независимого масштабирования платежного модуля
    • Разные команды работают над разными сервисами (платежи, аналитика, уведомления)
    • Требования к отказоустойчивости
// Пример сервиса уведомлений
// notification-service/index.js
const express = require('express');
const app = express();

app.post('/send', async (req, res) => {
  const { userId, type, message } = req.body;
  // Логика отправки email/push/SMS
  await sendNotification(userId, type, message);
  res.json({ success: true });
});

app.listen(3002, () => {
  console.log('Notification service running on port 3002');
});

// Общение через RabbitMQ
const amqp = require('amqplib');
async function consumeEvents() {
  const connection = await amqp.connect('amqp://localhost');
  const channel = await connection.createChannel();
  await channel.assertQueue('user.registered');

  channel.consume('user.registered', (msg) => {
    const user = JSON.parse(msg.content.toString());
    // Отправка welcome email
    sendWelcomeEmail(user.email);
    channel.ack(msg);
  });
}

Мой подход: Я выступаю за постепенную эволюцию. Начинаю с модульного монолита, где каждый модуль изолирован. Когда появляются четкие границы контекстов и операционные потребности, выделяю микросервисы. В последнем проекте мы начали с монолита, а через год выделили сервис аутентификации и сервис генерации отчетов в отдельные сервисы, когда их нагрузка стала существенно отличаться от основной системы.

Ответ 18+ 🔞

А, ну это классика, ёпта! Сидишь такой, думаешь: монолит или микросервисы? Как будто выбираешь — вилкой в глаз или в жопу раз. В общем, расскажу, как у меня это происходило, там просто овердохуища опыта набрался.

Первый проект: EdTech-стартап, где все только начиналось

  • Что выбрали: Монолит на Express.js. Просто, как палка.
  • Кто решал: Основатель и я, потому что я был там единственным заднеприводным разработчиком, остальные — фронтенд и дизайнер.
  • Почему так, а не иначе: Ну, тут всё ясно, как божий день. Нам нужно было выкатить работающую штуку на рынок быстрее, чем оппоненты успеют сказать «пизда рулю». Команда — три человека, включая кота. Деплой на Heroku в один клик. Одна кодовая база, чтобы не ебаться с кучей репозиториев, когда время — деньги. Подозрение ебать чувствую, что если бы мы тогда замикросервисились, мы бы до сих пор настройку Docker-компоуза делали, а проект бы накрылся медным тазом.
// Структура была проще пареной репы
project/
├── src/
│   ├── controllers/    # Кто что делает
│   ├── models/         # Сущности в базе
│   ├── routes/         # Куда стучаться
│   ├── middleware/     # Проверки, чтоб не лажали
│   └── utils/          # Всякая вспомогательная хрень
├── package.json
└── server.js          // Точка входа одна, голова не болит

Второй проект: Финансовая платформа, народ уже под 50 тысяч

  • Что выбрали: Микросервисы, ёперный театр.
  • Кто решал: Уже не я один, бля. Собрался целый синклит: техдир, архитектор, тимлиды. Обсуждения, презентации, доверия ебать ноль у каждого к предложениям другого.
  • Почему так: А вот тут уже припёрло. Платежный модуль надо масштабировать отдельно, потому что на него нагрузка дикая. Над аналитикой одна команда сидит, над уведомлениями — другая, чтобы не мешали друг другу. И если один сервис ляжет, чтоб всё остальное не посыпалось, как карточный домик.
// Вот, например, сервис уведомлений — живет своей жизнью
// notification-service/index.js
const express = require('express');
const app = express();

app.post('/send', async (req, res) => {
  const { userId, type, message } = req.body;
  // Логика отправки email/push/SMS
  await sendNotification(userId, type, message);
  res.json({ success: true });
});

app.listen(3002, () => {
  console.log('Notification service running on port 3002');
});

// А общаются они через RabbitMQ, как через почтовый голубятник
const amqp = require('amqplib');
async function consumeEvents() {
  const connection = await amqp.connect('amqp://localhost');
  const channel = await connection.createChannel();
  await channel.assertQueue('user.registered');

  channel.consume('user.registered', (msg) => {
    const user = JSON.parse(msg.content.toString());
    // Отправка welcome email
    sendWelcomeEmail(user.email);
    channel.ack(msg);
  });
}

А вот мой главный вывод, чувак: Не надо с порога решать, что ты архитектурный гений и будешь делать только микросервисы. Это хуй с горы. Я всегда за то, чтобы начинать с умного, модульного монолита. Сделай так, чтобы внутри всё было аккуратно разложено по полочкам. А потом, когда проект вырастет, и станет видно четкие границы (где нагрузка, где отдельная команда, где специфичные требования) — вот тогда и отрезай от монолита кусок в отдельный сервис. На последнем проекте мы так и сделали: год жили с монолитом, а потом спокойно выпилили аутентификацию и генерацию отчетов в отдельные сервисы, когда они стали вести себя как самостоятельные хитрые жопы. И все остались живы-здоровы.