← Назад к вопросам
Какие знаешь подходы для разбиения монолита?
3.0 Senior🔥 151 комментариев
#REST API и микросервисы
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Подходы для разбиения монолита на микросервисы
Миграция от монолитной архитектуры к микросервисам — сложный и долгий процесс. Вот проверенные стратегии, которые используют крупные компании.
1. Strangler Fig Pattern (Рекомендуется)
Это постепенное перехватывание функционала монолита новыми микросервисами:
// В API Gateway или бизнес-слое
public class RoutingLayer {
@PostMapping("/api/orders")
public ResponseEntity<Order> createOrder(@RequestBody CreateOrderRequest req) {
// Проверяем, живёт ли сервис уже отдельно
if (useNewOrderService()) {
return orderServiceClient.create(req); // микросервис
}
return legacyOrderService.create(req); // старый монолит
}
private boolean useNewOrderService() {
// Feature flag, A/B тестирование
return featureToggle.isEnabled("ORDER_SERVICE_V2");
}
}
Плюсы:
- Нулевой простой в production
- Можно откатиться в любой момент
- Постепенная миграция данных
Минусы:
- Требует промежуточного слоя (API Gateway)
- Усложнение логики маршрутизации
2. Domain-Driven Decomposition
Извлекаем сервисы по границам доменов (DDD Bounded Contexts):
Монолит
├── Orders Bounded Context → OrderService
├── Users Bounded Context → UserService
├── Payments Bounded Context → PaymentService
├── Inventory Bounded Context → InventoryService
└── Notifications Bounded Context → NotificationService
Процесс:
- Определить доменные границы (нарисовать на доске)
- Выделить сущности и агрегаты каждого домена
- Обозначить точки взаимодействия (события, API)
- Реализовать микросервис с собственной БД
// Orders микросервис
@Service
public class OrderService {
private final OrderRepository repo;
private final EventPublisher eventPublisher;
@Transactional
public OrderCreatedEvent createOrder(CreateOrderCmd cmd) {
Order order = new Order(cmd.getCustomerId(), cmd.getItems());
order.setStatus(OrderStatus.CREATED);
repo.save(order);
// Событие для других сервисов
eventPublisher.publish(new OrderCreatedEvent(order.getId()));
return ...;
}
}
Плюсы:
- Минимальные зависимости между сервисами
- Чистая архитектура
- Легче масштабировать
3. Strangler + Seams Model (Продвинутый подход)
Применяем Seams Model (по книге "Working Effectively with Legacy Code") для безопасного выделения функционала:
// Монолит: классический монолитный код
public class OrderService {
public Order createOrder(OrderRequest req) {
// Сложная логика с множеством зависимостей
validateOrder(req);
calculatePrice(req);
checkInventory(req);
processPayment(req);
sendNotification(req);
return order;
}
}
// Применяем Seam: подменяем зависимость
public class OrderServiceWithSeam extends OrderService {
@Override
protected void processPayment(OrderRequest req) {
// Вместо встроенной логики — вызываем микросервис
paymentServiceClient.process(req);
}
}
Так же выделяем:
- Inventory (склад)
- Notifications (уведомления)
- Billing (выставление счётов)
4. Database Per Service (Критическое решение)
Каждый микросервис имеет свою БД:
Ордеры Пользователи Платежи
↓ ↓ ↓
PostgreSQL PostgreSQL MongoDB
(Orders) (Users) (Payments)
Это решает:
- Независимый скейлинг БД
- Возможность менять СУБД под задачу
- Автономность сервиса
Проблемы, которые появляются:
- Распределённые транзакции — используй Event Sourcing + SAGA pattern:
@Service
public class OrderSagaOrchestrator {
@Transactional
public void createOrder(OrderRequest req) {
// Шаг 1: Создаём ордер
Order order = orderService.create(req);
try {
// Шаг 2: Резервируем товар
inventoryService.reserve(order.getItems());
// Шаг 3: Обрабатываем платёж
paymentService.charge(order.getPayment());
order.markAsConfirmed();
} catch (Exception e) {
// Откатываем все операции (compensating transactions)
inventoryService.release(order.getItems());
paymentService.refund(order.getPayment());
order.markAsFailed();
throw e;
}
}
}
- Data Consistency — используй Event Sourcing:
// OrderService публикует события
eventPublisher.publish(new OrderCreatedEvent(
orderId, customerId, items, createdAt
));
// UserService подписывается и обновляет view
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
userStatistics.incrementOrderCount(event.getCustomerId());
}
5. Incremental Database Migration
Постепенная миграция данных:
// Фаза 1: читаем из монолита, пишем в оба места
if (newServiceEnabled) {
newService.createOrder(req);
}
legacyService.createOrder(req);
// Фаза 2: читаем из нового сервиса, пишем в оба
Order order = newService.getOrder(id); // основной источник
if (order == null) {
order = legacyService.getOrder(id); // fallback
}
// Фаза 3: полностью мигрировались
Order order = newService.getOrder(id);
6. API Gateway как точка входа
Критично для управления миграцией:
@Component
public class OrderApiGateway {
@PostMapping("/api/v1/orders")
public ResponseEntity<OrderDTO> create(@RequestBody CreateOrderRequest req) {
if (useNewOrderService) {
return newOrderServiceClient.create(req);
}
return legacyOrderServiceClient.create(req);
}
}
Рекомендуемый порядок разбиения
- Выбери самый независимый модуль (например, Notifications)
- Реализуй Strangler + Gateway
- Мигрируй данные (двусторонняя синхронизация)
- Переключи трафик (используя Feature Flags)
- Повтори для следующего модуля
Инструменты, которые помогают
- Spring Cloud Config — централизованная конфигурация
- Spring Cloud Gateway — API Gateway
- RabbitMQ/Kafka — асинхронная коммуникация между сервисами
- Eureka — service discovery
- Feature Toggles (Unleash, LaunchDarkly) — контроль миграции
- ELK Stack — логирование распределённых запросов
Главное
Не старайся разбить всё сразу — начни с малого, проверь на production, затем масштабируй. Монолит разбирают годами, не неделями.