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

«Кто принимал решение между выбором монолита и микросервиса?» — вопрос из категории Софт-скиллы, который задают на 26% собеседований Node.js Разработчик. Ниже — развёрнутый пример ответа, который можно адаптировать под свой опыт.

Ответ

В моем опыте с 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);
  });
}

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