Skip to content

Микросервисы и архитектурные паттерны

Монолит vs Микросервисы

Монолитная архитектура

┌─────────────────────────────────────────────┐
│              МОНОЛИТ                         │
│  ┌─────────┬──────────┬──────────────┐      │
│  │  Auth   │  Orders  │   Products   │      │
│  │ Module  │  Module  │   Module     │      │
│  ├─────────┴──────────┴──────────────┤      │
│  │       Shared Database             │      │
│  └───────────────────────────────────┘      │
│              Один процесс                    │
│              Один деплой                     │
│              Одна кодовая база               │
└─────────────────────────────────────────────┘

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

  • Простая разработка и отладка
  • Простой деплой (один артефакт)
  • Нет сетевых задержек между модулями
  • Транзакции в одной БД

Недостатки монолита:

  • Масштабируется только целиком
  • Сбой в одном модуле ломает всё
  • Сложность растёт экспоненциально
  • Долгие циклы деплоя

Микросервисная архитектура

┌──────────┐   ┌──────────┐   ┌──────────────┐
│   Auth   │   │  Orders  │   │   Products   │
│ Service  │   │ Service  │   │   Service    │
│ (Node.js)│   │ (Go)     │   │   (Python)   │
├──────────┤   ├──────────┤   ├──────────────┤
│  Auth DB │   │ Orders DB│   │  Products DB │
│ (Redis)  │   │(Postgres)│   │  (MongoDB)   │
└──────────┘   └──────────┘   └──────────────┘
     ↕               ↕               ↕
  ──────── API Gateway / Message Broker ────────

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

  • Независимое масштабирование каждого сервиса
  • Технологическая свобода (polyglot)
  • Изолированные сбои
  • Маленькие команды, быстрый деплой

Недостатки:

  • Сложность распределённой системы
  • Сетевые задержки
  • Согласованность данных (eventual consistency)
  • Мониторинг и отладка сложнее

Когда переходить на микросервисы

Правило

Не начинайте с микросервисов! Начните с монолита и выделяйте сервисы по мере необходимости.

┌─────────────────────────────────────────────────────────┐
│  Стоит переходить, если:                                │
│                                                          │
│  ✓ Команда > 10-15 разработчиков                        │
│  ✓ Разные модули масштабируются по-разному              │
│  ✓ Нужна независимость деплоя                           │
│  ✓ Разные требования к технологиям                      │
│  ✓ Модули имеют чёткие границы (bounded contexts)       │
├─────────────────────────────────────────────────────────┤
│  НЕ стоит, если:                                        │
│                                                          │
│  ✗ Маленькая команда (1-5 человек)                      │
│  ✗ Стартап на ранней стадии                             │
│  ✗ Нет опыта работы с распределёнными системами         │
│  ✗ Границы между модулями нечёткие                      │
└─────────────────────────────────────────────────────────┘

Коммуникация между сервисами

Синхронная (REST / gRPC)

Сервис A ────HTTP/REST────→ Сервис B
           ←──── Ответ ─────

Сервис A ────gRPC──────→ Сервис B
           ←── Ответ ────

REST:
  + Простой, понятный (JSON over HTTP)
  + Широкая поддержка
  - Больше накладных расходов (JSON parsing)

gRPC:
  + Быстрее (Protocol Buffers, HTTP/2)
  + Строгая типизация (proto файлы)
  + Стриминг
  - Сложнее отлаживать

Асинхронная (Message Broker)

                  ┌─────────────────┐
Сервис A ──msg──→│  Message Broker  │──msg──→ Сервис B
                  │  (Kafka/RabbitMQ)│──msg──→ Сервис C
                  └─────────────────┘──msg──→ Сервис D

Преимущества:
  + Сервисы не зависят друг от друга
  + Буферизация при пиковых нагрузках
  + Гарантия доставки
  + Реплей сообщений (Kafka)

Недостатки:
  - Eventual consistency
  - Сложнее отладка
  - Дополнительная инфраструктура

Паттерны обмена сообщениями

1. Request-Reply:
   A ──request──→ Queue ──→ B
   A ←──reply───← Queue ←── B

2. Publish-Subscribe:
   Publisher ──→ Topic ──→ Subscriber 1
                      ──→ Subscriber 2
                      ──→ Subscriber 3

3. Event-Driven:
   Order Service ──"OrderCreated"──→ Event Bus
   Payment Service ←─────────────────── (listens)
   Notification Service ←────────────── (listens)
   Inventory Service ←──────────────── (listens)

API Gateway

Единая точка входа для всех клиентских запросов.

┌──────────┐   ┌──────────┐   ┌──────────┐
│  Web App │   │Mobile App│   │ Partner  │
└────┬─────┘   └────┬─────┘   └────┬─────┘
     │              │              │
     └──────────────┼──────────────┘

           ┌────────▼────────┐
           │   API Gateway   │
           │                 │
           │ • Routing       │
           │ • Auth          │
           │ • Rate Limiting │
           │ • Load Balance  │
           │ • Caching       │
           │ • Logging       │
           └────────┬────────┘

        ┌───────────┼───────────┐
        ↓           ↓           ↓
   ┌─────────┐ ┌─────────┐ ┌─────────┐
   │ Users   │ │ Orders  │ │Products │
   │ Service │ │ Service │ │ Service │
   └─────────┘ └─────────┘ └─────────┘

Популярные решения:

  • Kong
  • AWS API Gateway
  • Nginx (как reverse proxy)
  • Traefik

Service Discovery

Как сервисы находят друг друга в динамическом окружении.

Client-Side Discovery:
  ┌──────────┐    1. Запрос    ┌───────────────┐
  │  Сервис A│ ──────────────→│  Service      │
  │          │ ←──────────────│  Registry     │
  │          │   2. Список     │ (Consul/Etcd) │
  │          │   инстансов     └───────────────┘
  │          │
  │          │    3. Прямой запрос
  │          │ ──────────────→ Сервис B (instance 2)
  └──────────┘

Server-Side Discovery:
  ┌──────────┐    1. Запрос    ┌───────────────┐
  │  Сервис A│ ──────────────→│ Load Balancer │
  └──────────┘                │               │
                              │ 2. Маршрутиз. │
                              └───────┬───────┘

                        ┌─────────────┼──────────────┐
                        ↓             ↓              ↓
                   Сервис B      Сервис B       Сервис B
                   (inst. 1)     (inst. 2)      (inst. 3)

Паттерны отказоустойчивости

Circuit Breaker

Предотвращает каскадные сбои, "размыкая цепь" при частых ошибках.

                  ┌─────────────┐
                  │   CLOSED    │  ← Нормальная работа
                  │ (замкнут)   │    Запросы проходят
                  └──────┬──────┘
                         │ Ошибки > порога

                  ┌─────────────┐
                  │    OPEN     │  ← Цепь разомкнута
                  │ (разомкнут) │    Запросы отклоняются
                  └──────┬──────┘    (fail fast)
                         │ Таймаут

                  ┌─────────────┐
                  │  HALF-OPEN  │  ← Пробные запросы
                  │ (полуоткрыт)│    Если OK → CLOSED
                  └─────────────┘    Если ошибка → OPEN

Retry с Exponential Backoff

Попытка 1: сразу
Попытка 2: через 1 сек
Попытка 3: через 2 сек
Попытка 4: через 4 сек
Попытка 5: через 8 сек  ← max backoff

+ Jitter (случайная задержка) для предотвращения
  "thundering herd" — когда все клиенты повторяют
  одновременно

Bulkhead (Переборка)

Изоляция ресурсов, чтобы сбой в одном компоненте не затронул другие.

┌─────────────────────────────────────────────┐
│  Thread Pool: Orders (20 потоков)            │
│  ████████████████████                       │
├─────────────────────────────────────────────┤
│  Thread Pool: Payments (10 потоков)          │
│  ██████████  ← Если Payment завис,          │
│                 Orders продолжает работать  │
├─────────────────────────────────────────────┤
│  Thread Pool: Notifications (5 потоков)      │
│  █████                                      │
└─────────────────────────────────────────────┘

Saga Pattern

Управление распределёнными транзакциями через последовательность локальных транзакций.

Хореография (Event-Based)

Order       Payment     Inventory    Shipping
Service     Service     Service      Service
  │            │            │            │
  │ OrderCreated           │            │
  ├──────────→│            │            │
  │           │ PaymentProcessed        │
  │           ├──────────→│            │
  │           │           │ ItemsReserved
  │           │           ├──────────→│
  │           │           │           │ ShippingScheduled
  │           │           │           ├──→ Done ✓

Компенсация при ошибке:
  │           │           │ InventoryFailed
  │           │           ├──── X
  │           │ RefundIssued            │
  │           ├←──────────│            │
  │ OrderCancelled         │            │
  ├←──────────│            │            │

Оркестрация (Orchestrator)

              ┌──────────────┐
              │  Saga        │
              │ Orchestrator │
              └──────┬───────┘

          ┌──────────┼──────────┬──────────┐
          ↓          ↓          ↓          ↓
     ┌─────────┐┌─────────┐┌─────────┐┌─────────┐
     │ Order   ││ Payment ││Inventory││Shipping │
     │ Service ││ Service ││ Service ││ Service │
     └─────────┘└─────────┘└─────────┘└─────────┘

Orchestrator управляет порядком шагов
и компенсациями при ошибках

Архитектурные стили

Event-Driven Architecture

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│   Producer   │────→│  Event Bus   │────→│  Consumer    │
│              │     │  (Kafka)     │────→│  Consumer    │
│ Генерирует   │     │              │────→│  Consumer    │
│ события      │     │ Хранит и     │     │              │
│              │     │ маршрутизир. │     │ Реагируют    │
└──────────────┘     └──────────────┘     └──────────────┘

Serverless

Событие → Function → Результат

┌───────────┐    ┌─────────────┐    ┌──────────┐
│ HTTP запрос│──→│ Lambda/     │──→│ DynamoDB │
│ S3 upload  │──→│ Cloud Func  │──→│ S3       │
│ Timer      │──→│ Azure Func  │──→│ SQS      │
│ Queue msg  │──→│             │──→│ Email    │
└───────────┘    └─────────────┘    └──────────┘

+ Нет управления серверами
+ Автоматическое масштабирование
+ Оплата за выполнение (pay-per-use)
- Cold start
- Ограничения по времени выполнения
- Vendor lock-in

Sidecar Pattern

┌─────────────────────────────────────┐
│              Pod                     │
│  ┌─────────────┐  ┌──────────────┐  │
│  │   Main      │  │   Sidecar    │  │
│  │  Container  │  │  Container   │  │
│  │  (App)      │←→│  (Envoy/     │  │
│  │             │  │   Istio)     │  │
│  └─────────────┘  └──────────────┘  │
│                                      │
│  Sidecar обеспечивает:               │
│  • Service mesh                      │
│  • mTLS шифрование                   │
│  • Retry/Circuit Breaker             │
│  • Observability                     │
└─────────────────────────────────────┘

Observability (Наблюдаемость)

Три столпа наблюдаемости в микросервисной архитектуре:

┌────────────────┬────────────────┬────────────────┐
│    Логи        │    Метрики     │    Трейсы      │
│  (Logs)        │  (Metrics)     │  (Traces)      │
├────────────────┼────────────────┼────────────────┤
│ Что случилось  │ Как работает   │ Где проблема   │
│                │                │                │
│ ELK Stack      │ Prometheus     │ Jaeger         │
│ (Elastic,      │ + Grafana      │ Zipkin         │
│  Logstash,     │                │ OpenTelemetry  │
│  Kibana)       │ DataDog        │                │
│                │                │                │
│ Structured     │ RED:           │ Distributed    │
│ logging        │ Rate, Errors,  │ tracing        │
│ (JSON)         │ Duration       │ (TraceID)      │
└────────────────┴────────────────┴────────────────┘

Distributed Tracing

Request: GET /api/order/123
TraceID: abc-123

Service A (API Gateway)  ──────────────────────── 120ms

  ├─ Service B (Orders)  ────────────────── 80ms
  │    │
  │    ├─ Service C (Users)  ──────── 20ms
  │    │
  │    └─ Service D (Products) ───── 30ms

  └─ Service E (Cache)  ──── 5ms

→ Видим полный путь запроса через все сервисы
→ Определяем узкие места

Вопросы на собеседовании

1. Как бы вы разделили монолит на микросервисы?

Шаг 1: Определить bounded contexts (DDD)
Шаг 2: Выделить наименее связанный модуль
Шаг 3: Strangler Fig Pattern:
   ┌──────────────────────────────────┐
   │  API Gateway / Proxy             │
   │  /users → Новый User Service     │
   │  /orders → Старый монолит        │ ← Постепенная миграция
   │  /products → Старый монолит      │
   └──────────────────────────────────┘
Шаг 4: Разделить БД
Шаг 5: Повторить для следующего модуля

2. Как обеспечить согласованность данных между сервисами?

Варианты:
1. Saga Pattern — цепочка локальных транзакций + компенсации
2. Event Sourcing — источник истины в событиях
3. Outbox Pattern — записать событие в ту же транзакцию
4. Two-Phase Commit (2PC) — строгая согласованность
   (обычно избегают из-за производительности)

3. Как организовать CI/CD для микросервисов?

Mono-repo:
  + Единый CI/CD pipeline
  + Простой рефакторинг
  - Сложная система сборки

Poly-repo (отдельный репозиторий на сервис):
  + Независимые pipelines
  + Чёткие границы ответственности
  - Сложнее шарить код
  - Версионирование зависимостей

Резюме

┌─────────────────────────────────────────────────────────┐
│  1. Начинайте с монолита, переходите на микросервисы    │
│     при необходимости                                   │
│  2. Определите чёткие границы сервисов (DDD)            │
│  3. Используйте async коммуникацию где возможно         │
│  4. Реализуйте Circuit Breaker и Retry                  │
│  5. Saga Pattern для распределённых транзакций           │
│  6. API Gateway как единая точка входа                  │
│  7. Observability: логи + метрики + трейсы              │
│  8. Автоматизируйте деплой (CI/CD per service)          │
└─────────────────────────────────────────────────────────┘