← Назад к вопросам

Больше опыта в работе с монолитом или с микросервисом

1.3 Junior🔥 191 комментариев

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Монолит vs Микросервисы: мой практический опыт

За свою карьеру я работал с обоими подходами, и оба имеют свои сильные и слабые стороны. Выбор архитектуры — это не религия, а pragmatичное решение в зависимости от контекста.

Мой опыт с Монолитом

Первые 4 года я разрабатывал большой монолитный backend для e-commerce платформы (500K+ активных пользователей).

Архитектура:

┌─────────────────────────────────────┐
│      Single Node.js Application     │
├─────────────────────────────────────┤
│                                     │
│  ├─ User API (10k RPS)              │
│  ├─ Product API (30k RPS)           │
│  ├─ Order API (5k RPS)              │
│  ├─ Payment Processing (1k RPS)     │
│  ├─ Search Service (15k RPS)        │
│  ├─ Notification Service (2k RPS)   │
│  ├─ Analytics (3k RPS)              │
│  └─ Admin Panel                     │
│                                     │
└─────────────────────────────────────┘
         ↓
    [PostgreSQL]
    [Redis Cache]
    [Elasticsearch]

Структура проекта:

src/
├── controllers/
│   ├── users.ts
│   ├── products.ts
│   ├── orders.ts
│   ├── payments.ts
│   ├── notifications.ts
│   ├── search.ts
│   └── analytics.ts
├── services/
│   ├── user.service.ts
│   ├── product.service.ts
│   ├── order.service.ts
│   ├── payment.service.ts
│   ├── notification.service.ts
│   ├── search.service.ts
│   └── analytics.service.ts
├── repositories/
│   ├── user.repository.ts
│   ├── product.repository.ts
│   ├── order.repository.ts
│   └── ...
├── models/
├── middleware/
├── utils/
└── main.ts

Преимущества монолита:

// 1. Простота при начале
const app = express();
app.get('/api/users', handlers.getUsers);  // Начать просто
app.listen(3000);

// 2. Транзакции между сервисами
const createOrder = async (userId, items) => {
  const connection = await pool.getConnection();
  try {
    // Все в одной транзакции
    await connection.query('BEGIN');
    
    const order = await connection.query(
      'INSERT INTO orders (user_id) VALUES ($1) RETURNING id',
      [userId]
    );
    
    const items = await connection.query(
      'INSERT INTO order_items (order_id, product_id) VALUES ...',
      [...]
    );
    
    await connection.query('COMMIT');
  } catch (err) {
    await connection.query('ROLLBACK');
  }
};

// 3. Один стек, одна кодовая база
const sharedUtil = require('./utils/formatting');  // Везде один util

// 4. Простой DevOps
// docker run my-app
// docker scale my-app=3  // Три инстанса монолита

Проблемы монолита:

// 1. Когда разные части требуют разных технологий
const issues = {
  searchService: {
    technology: 'Elasticsearch',
    scaling: 'Separate cluster',
    language: 'Java is better',
    problemInMonolith: 'Вынуждены использовать Node.js'
  },
  machinelearning: {
    technology: 'Python + TensorFlow',
    language: 'Python is essential',
    problemInMonolith: 'Не можем использовать Python'
  },
  heavyComputation: {
    technology: 'Go для speed',
    problemInMonolith: 'Узкие места в Node.js'
  }
};

// 2. One deployment = all or nothing
// Исправляем баг в Payment Service → перезагружаем всё приложение
// Все User API запросы падают на время deployment

// 3. Разные масштабируемость потребности
// Product API: 30k RPS нужна масштабируемость
// Payment API: 1k RPS, но нужна reliability
// Масштабируем всё как одно целое

// 4. Когда 500 файлов в models/, 300 в services/
// Становится сложно ориентироваться

// 5. Database coupling
// Все сервисы ходят в одну БД
// Сложно optimize каждый сервис отдельно
const slowQueries = [
  'SELECT * FROM users u JOIN orders o ON u.id = o.user_id',  // Нужен индекс
  'SELECT * FROM products WHERE category = ?',  // N+1 query
  'SELECT COUNT(*) FROM notifications'  // Full table scan
];

Мой опыт с Микросервисами

Последние 6 лет я разрабатывал систему из микросервисов с 100+ сервисов в одной компании.

Архитектура:

┌──────────────────────────────────────────────────────────────┐
│                      API Gateway                             │
└──────────────────────────────────────────────────────────────┘
   ↓        ↓        ↓        ↓         ↓         ↓
┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
│User │ │Order│ │Pay- │ │Notif│ │Sear-│ │Ana- │
│Ser- │ │Ser- │ │ment │ │ica- │ │ch   │ │lyt- │
│vice │ │vice │ │Ser- │ │tion │ │Ser- │ │ics  │
│     │ │     │ │vice │ │Ser- │ │vice │ │Ser- │
│     │ │     │ │     │ │vice │ │     │ │vice │
└─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘
   ↓        ↓        ↓        ↓         ↓         ↓
┌─────────────────────────────────────────────────┐
│        Message Broker (RabbitMQ / Kafka)        │
└─────────────────────────────────────────────────┘

Структура каждого сервиса:

user-service/
├── src/
│   ├── domain/
│   │   └── user.entity.ts
│   ├── application/
│   │   └── create-user.usecase.ts
│   ├── infrastructure/
│   │   ├── user.repository.ts
│   │   ├── kafka-producer.ts
│   │   └── jwt-auth.ts
│   ├── presentation/
│   │   └── user.controller.ts
│   └── main.ts
├── Dockerfile
├── docker-compose.yml
└── package.json

Преимущества микросервисов:

// 1. Независимое масштабирование
kubectl scale deployment/user-service --replicas=5
kubectl scale deployment/payment-service --replicas=2
kubectl scale deployment/search-service --replicas=10

// 2. Independent deployment
// Изменение в User Service не трогает Order Service
kubectl rollout status deployment/user-service
// Order Service продолжает работать

// 3. Свобода в выборе технологии
services = {
  user_service: 'Node.js + TypeScript',
  search_service: 'Go + Elasticsearch',
  ml_service: 'Python + FastAPI',
  payment_service: 'Java + Spring'
}

// 4. Четкие границы ответственности
// User Service отвечает только за пользователей
// Order Service отвечает только за заказы
interface UserServiceAPI {
  createUser(data: CreateUserDTO): Promise<User>
  getUser(id: string): Promise<User>
  deleteUser(id: string): Promise<void>
}

// 5. Асинхронная коммуникация
const orderService = () => {
  const kafka = new Kafka({ brokers: ['kafka:9092'] });
  const producer = kafka.producer();

  return async (userId: string, items: any[]) => {
    // Создаем заказ
    const order = { id: uuid(), userId, items };

    // Публикуем событие
    await producer.send({
      topic: 'order.created',
      messages: [{ value: JSON.stringify(order) }]
    });

    // Не ждем, пока Payment Service обработает
    // Notification Service сам подпишется и отправит письмо
    // Analytics Service сам обновит метрики
  };
};

// 6. Отказоустойчивость
// Если Payment Service упал, остальное работает
// С монолитом падает всё

Проблемы микросервисов:

// 1. Сложность распределенных транзакций
// Нельзя сделать ACID транзакцию через несколько сервисов
const createOrderComplexity = async () => {
  // Шаг 1: создать заказ
  const order = await orderService.create(...);
  
  // Шаг 2: зарезервировать товар
  try {
    await inventoryService.reserve(order.items);
  } catch (err) {
    // Ототдели заказ? Но он уже был создан!
    // Нужна компенсирующая транзакция (Saga паттерн)
    await orderService.cancel(order.id);
    throw err;
  }
  
  // Шаг 3: обработать платеж
  try {
    await paymentService.charge(...);
  } catch (err) {
    // Откатить резерв товара
    await inventoryService.release(order.items);
    // Отменить заказ
    await orderService.cancel(order.id);
    throw err;
  }
};

// 2. Трудная отладка (Debugging nightmare)
// Request прошел через 5 сервисов
// Логи разбросаны по разным серверам
// Нужна централизованная система трассировки (Jaeger, Datadog)

// 3. Network latency
// Монолит: инсталль в памяти
// Микросервис: HTTP call, сетевая задержка
const latencyComparison = {
  monolith: 'service.getUser() // <1ms',
  microservice: `
    await http.get('http://user-service:3000/users/123')  // 5-50ms
    + потенциальные retry-и
    + потенциальные timeout-ы
  `
};

// 4. DevOps complexity
// Нужно управлять 100+ сервисами
// Kubernetes, Docker Compose, monitoring, alerting

// 5. Данные разбросаны по разным БД
// Сложный анализ и отчеты
const complexReport = `
  SELECT u.name, COUNT(o.id) as orders_count
  FROM users u
  LEFT JOIN orders o ON u.id = o.user_id  -- Но они в разных БД!
  GROUP BY u.id
`;

Сравнение

КритерийМонолитМикросервисы
ComplexityНизкаяВысокая
Speed to MVPБыстроДолго
Independent deploymentНетДа
ScalingВсе вместеНезависимо
Technology diversityОграниченноПолная свобода
TestingПростоСложно (интеграция)
Team coordination1 командаМного команд
DebuggingПростоСложно
CostНизкийВысокий

Мой рекомендуемый путь

Start with Monolith → Migrate to Microservices when needed

const evolutionPath = {
  stage_1: {
    when: 'MVP',
    architecture: 'Monolith',
    reasoning: 'Быстро выводим на рынок',
    team_size: '1-3 разработчика'
  },
  stage_2: {
    when: '10K+ users, bottleneck found',
    architecture: 'Modular Monolith',
    reasoning: 'Разделяем логику, но один deployment',
    team_size: '5-10 разработчиков'
  },
  stage_3: {
    when: '100K+ users, разные потребности в масштабировании',
    architecture: 'Microservices + API Gateway',
    reasoning: 'Разделяем сервисы и deployments',
    team_size: '10+ разработчиков'
  },
  stage_4: {
    when: '1M+ users, очень сложная система',
    architecture: 'Distributed Microservices + Event-driven',
    reasoning: 'Асинхронность, асинхронность, асинхронность',
    team_size: '50+ разработчиков'
  }
};

Вывод

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