Был ли последний проект монолитом или микросервисом
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# Монолит 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%
Что бы я сделал по-другому
- Не выделял бы полностью — гибридный подход лучше
- Выделил бы раньше — когда монолит стал 50K строк (было 100K)
- Инвестировал бы в observability — логирование и мониторинг критичны
- Не использовал бы REST — использовал бы gRPC для межсервисного взаимодействия
- Документировал бы контракты — как сервисы общаются
Рекомендация по выбору архитектуры
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
- Когда время разработки критично
- Когда не ясны требования
- Маленьких команд
Микросервисы хороши для:
- Масштабных приложений с миллионами пользователей
- Независимых компонентов
- Разных требований к масштабированию
- Больших распределённых команд
Не нужно выбирать между ними — эволюционируй архитектуру вместе с приложением.