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

Был ли последний проект монолитом или микросервисом

1.0 Junior🔥 191 комментариев
#Архитектура и паттерны

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

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

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

# Монолит vs Микросервисы: опыт из последних проектов

Последний проект, над которым я активно работал, начался как монолит и эволюционировал в гибридную архитектуру. Это очень типичный путь для растущих стартапов.

Первая фаза: монолит (MVP, первые 6 месяцев)

Начинали со стандартного монолитного приложения на Django:

┌─────────────────────────────────────────┐
│     Monolithic Django Application       │
├─────────────────────────────────────────┤
│ ├─ Users & Auth                         │
│ ├─ Products & Catalog                   │
│ ├─ Orders & Payments                    │
│ ├─ Notifications                        │
│ ├─ Reporting & Analytics                │
│ └─ Admin Panel                          │
└─────────────────────────────────────────┘
        ↓
    PostgreSQL

Почему монолит?

Плюсы на этапе MVP:

  • ✅ Быстро разрабатывать — всё в одном месте
  • ✅ Просто деплоить — один сервис
  • ✅ Легко дебагить — вся логика в одном процессе
  • ✅ Легко масштабировать горизонтально (копировать инстансы)
  • ✅ Транзакции работают из коробки
  • ✅ Меньше операционной сложности

Что я делал:

# Django models
from django.db import models

class User(models.Model):
    email = models.EmailField(unique=True)
    name = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)

class Product(models.Model):
    name = models.CharField(max_length=255)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    seller = models.ForeignKey(User, on_delete=models.CASCADE)

class Order(models.Model):
    buyer = models.ForeignKey(User, on_delete=models.CASCADE)
    products = models.ManyToManyField(Product)
    created_at = models.DateTimeField(auto_now_add=True)

# Views
from django.shortcuts import render
from django.views import View

class OrderCreateView(View):
    def post(self, request):
        # Вся логика в одном месте
        validate_order(request.data)
        order = create_order(request.user, request.data)
        send_confirmation_email(order)  # В том же процессе
        update_inventory(order)         # В том же процессе
        return Response(order_serializer(order).data)

Проблемы, которые появились (6-12 месяцев)

Когда монолит вырос, столкнулись с проблемами:

1. Очереди и асинхронность

# Проблема: отправка email блокирует ответ
class OrderCreateView(View):
    def post(self, request):
        order = Order.objects.create(...)
        send_email(order)  # БЛОКИРУЕТ на 5 секунд!
        return Response(...)  # Пользователь ждёт

2. Масштабирование разных компонентов

Проблема:
├─ API сервер нужен 2 инстанса (CPU-bound)
├─ Notification worker нужен 10 инстансов (IO-bound)
├─ Reporting нужен 1 инстанс (Memory-bound)
└─ Но все они в одном процессе → копируем всех!

3. Развёртывание и разработка

Если изменить notification логику →
Повторно тестировать ВСЁ (auth, products, orders, ...)
Деплоим ВСЕ компоненты сразу

4. Production issues

# Реальный случай:
# Один медленный batch job вызвал утечку памяти
# Это упал ВСЕ приложение (auth, products, all features)
# Потребовалось 40 минут на восстановление

Вторая фаза: частичный переход на микросервисы (месяцы 12-18)

Решили эволюционировать архитектуру. Не стали делать full microservices сразу, а выделили критические сервисы:

┌──────────────────────────────────────────────────────┐
│            API Gateway (nginx)                       │
└────────┬─────────────┬──────────────┬────────────────┘
         │             │              │
    ┌────▼────┐  ┌─────▼────┐  ┌─────▼──────┐
    │ Monolith │  │ Payments │  │ Notifications
    │ (Core)   │  │ Service  │  │ Service
    │          │  │(FastAPI) │  │ (Celery + Redis)
    │ - Auth   │  │          │  │
    │ - Users  │  │ - Stripe │  │ - Email
    │ - Orders │  │ - Webhooks  │ - SMS
    │ - etc    │  │          │  │ - Push
    └────┬─────┘  └──────────┘  └─────────────┘
         │
    PostgreSQL
    
    Message Queue (RabbitMQ)

Что я выделил в микросервисы?

1. Payments Service (FastAPI)

# Separate service для обработки платежей
from fastapi import FastAPI
import stripe

app = FastAPI()

@app.post("/api/payments/create")
async def create_payment(order_id: int, amount: float):
    # Stripe логика отделена
    try:
        charge = stripe.Charge.create(
            amount=int(amount * 100),
            currency="usd",
            source="tok_visa"
        )
        return {"success": True, "charge_id": charge.id}
    except stripe.error.CardError as e:
        return {"error": e.user_message}

# Webhook для получения обновлений от Stripe
@app.post("/api/payments/webhook")
async def stripe_webhook(request):
    # Обновляем статус платежа
    pass

2. Notifications Service (Celery + Redis)

# Асинхронная отправка уведомлений
from celery import Celery
import smtplib

app = Celery('notifications')

@app.task
def send_order_confirmation_email(order_id):
    # Выполняется асинхронно
    order = fetch_order_from_main_db(order_id)
    send_email(
        to=order.user.email,
        subject=f"Order {order.id} confirmed",
        body=render_template(order)
    )

@app.task
def send_sms_notification(user_id, message):
    # Отправка SMS асинхронно
    user = fetch_user_from_main_db(user_id)
    sms_client.send(user.phone, message)

Как они общаются?

# В основном монолите
from django.db import models
from celery import shared_task
import requests

class Order(models.Model):
    status = models.CharField(...)
    
class OrderCreateView(View):
    def post(self, request):
        # Создаём заказ в монолите
        order = Order.objects.create(...)
        
        # Асинхронно запускаем задачи
        send_order_confirmation_email.delay(order.id)  # Celery
        
        # Синхронно вызываем payment service
        response = requests.post(
            'http://payment-service/api/payments/create',
            json={'order_id': order.id, 'amount': order.total}
        )
        
        if response.json()['success']:
            order.status = 'paid'
            order.save()
            return Response(...)

Третья фаза: стабилизация (месяцы 18+)

После выделения критических сервисов архитектура стабилизировалась:

Текущая топология

┌────────────────────────────────────────────┐
│         Nginx (Load Balancer)              │
└────────┬─────────────────────────────────┬─┘
         │                                 │
    ┌────▼────────┐              ┌────────▼─────┐
    │ Monolith x3 │              │ Services x2  │
    │ (Django)    │              │ (FastAPI)    │
    │ Port 8000   │              │ Port 8001    │
    └────┬────────┘              └────────┬─────┘
         │                                 │
    ┌────▼─────────────────────────────────▼┐
    │      PostgreSQL (RDS)                 │
    │      - Main database                  │
    │      - Replicas for read-heavy ops    │
    └─────────────────────────────────────┘
    
    ┌─────────────────────────────┐
    │  Message Queue (RabbitMQ)   │
    │  - Order events             │
    │  - Notifications queue      │
    │  - Webhooks queue           │
    └────┬────────────────────────┘
         │
    ┌────▼────────────┐
    │ Celery Workers  │
    │ - Email sender  │
    │ - SMS sender    │
    │ - Report gen    │
    └─────────────────┘
    
    ┌──────────────────┐
    │ Redis Cache      │
    │ - Session cache  │
    │ - Rate limiting  │
    └──────────────────┘

Что я извлёк из этого опыта

Правила выделения микросервисов

Выделяй сервис, если:

  • Имеет свой жизненный цикл (можно развёртывать отдельно)
  • Имеет разные требования к масштабированию
  • Команда может разрабатывать независимо
  • Есть защита от сбоев других сервисов (circuit breaker)

Не выделяй в микросервис:

  • Если очень много межсервисного взаимодействия
  • Если требуются ACID транзакции (монолит лучше)
  • Если никто не будет его разрабатывать отдельно
  • Если команда слишком мала (< 5 backend разработчиков)

Метрики успеха

До микросервисов:
- Deployment: 45 минут
- Mean Time To Recovery (MTTR): 40 минут
- Test suite: 15 минут
- Вероятность break something: 35%

После микросервисов:
- Deployment: 5 минут (отдельный сервис)
- MTTR: 5 минут (изолированный сбой)
- Test suite: 3 минуты (меньше кода)
- Вероятность break something: 5%

Что бы я сделал по-другому

  1. Не выделял бы полностью — гибридный подход лучше
  2. Выделил бы раньше — когда монолит стал 50K строк (было 100K)
  3. Инвестировал бы в observability — логирование и мониторинг критичны
  4. Не использовал бы REST — использовал бы gRPC для межсервисного взаимодействия
  5. Документировал бы контракты — как сервисы общаются

Рекомендация по выбору архитектуры

if is_mvp or team_size < 3:
    architecture = "Monolith"  # Быстрее разрабатывать
elif revenue > 1M and team_size > 10:
    architecture = "Microservices"  # Масштабируемость критична
else:
    architecture = "Hybrid"  # Best of both worlds

Заключение

Монолит хорош для:

  • Стартапов и MVP
  • Когда время разработки критично
  • Когда не ясны требования
  • Маленьких команд

Микросервисы хороши для:

  • Масштабных приложений с миллионами пользователей
  • Независимых компонентов
  • Разных требований к масштабированию
  • Больших распределённых команд

Не нужно выбирать между ними — эволюционируй архитектуру вместе с приложением.