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

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

1.3 Junior🔥 191 комментариев
#Soft skills и опыт работы#Архитектура и паттерны

Комментарии (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 MonolithMigrate 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+ разработчиков'
  }
};

Вывод

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

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