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

Как оркестрировался бизнес-флоу в микросервисах на последней работе

3.0 Senior🔥 181 комментариев
#REST API и микросервисы#SOLID и паттерны проектирования#Брокеры сообщений

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

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

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

# Оркестрация бизнес-флоу в микросервисах

На последней работе я использовал несколько подходов для оркестрации сложных бизнес-процессов между микросервисами. Расскажу о наиболее значимом проекте.

Контекст: система обработки заказов

Мы разработали систему электронной коммерции с микросервисной архитектурой:

  • Order Service — управление заказами
  • Payment Service — обработка платежей
  • Inventory Service — управление складом
  • Notification Service — отправка уведомлений
  • Shipping Service — доставка

Подход 1: Оркестрация через Saga Pattern

Использовал Saga pattern для управления распределёнными транзакциями.

Choreography-based Saga

// Order Service
@Service
public class OrderService {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    @Transactional
    public Order createOrder(CreateOrderRequest request) {
        Order order = new Order(request);
        order.setStatus(OrderStatus.PENDING);
        orderRepository.save(order);
        
        // Отправляем событие
        rabbitTemplate.convertAndSend(
            "order.exchange",
            "order.created",
            new OrderCreatedEvent(order.getId(), order.getTotalAmount())
        );
        return order;
    }
}

// Payment Service слушает события
@Service
public class PaymentService {
    @RabbitListener(queues = "payment.queue")
    @Transactional
    public void handleOrderCreated(OrderCreatedEvent event) {
        try {
            Payment payment = processPayment(event.getOrderId(), event.getAmount());
            
            rabbitTemplate.convertAndSend(
                "order.exchange",
                "payment.completed",
                new PaymentCompletedEvent(event.getOrderId(), payment.getId())
            );
        } catch (PaymentException e) {
            rabbitTemplate.convertAndSend(
                "order.exchange",
                "payment.failed",
                new PaymentFailedEvent(event.getOrderId(), e.getMessage())
            );
        }
    }
}

// Inventory Service реагирует на успешный платёж
@Service
public class InventoryService {
    @RabbitListener(queues = "inventory.queue")
    @Transactional
    public void handlePaymentCompleted(PaymentCompletedEvent event) {
        try {
            reserveItems(event.getOrderId());
            rabbitTemplate.convertAndSend(
                "order.exchange",
                "inventory.reserved",
                new InventoryReservedEvent(event.getOrderId())
            );
        } catch (InsufficientStockException e) {
            // Компенсирующая транзакция: откатить платёж
            rabbitTemplate.convertAndSend(
                "order.exchange",
                "order.failed",
                new OrderFailedEvent(event.getOrderId())
            );
        }
    }
}

Подход 2: Оркестрация через Orchestrator (Service)

Создал отдельный Order Orchestrator для управления флоу.

@Service
public class OrderOrchestrator {
    @Autowired
    private OrderServiceClient orderClient;
    
    @Autowired
    private PaymentServiceClient paymentClient;
    
    @Autowired
    private InventoryServiceClient inventoryClient;
    
    @Autowired
    private ShippingServiceClient shippingClient;
    
    @Transactional
    public Order executeOrderFlow(CreateOrderRequest request) {
        // Шаг 1: Создаём заказ
        Order order = orderClient.createOrder(request);
        
        try {
            // Шаг 2: Обрабатываем платёж
            Payment payment = paymentClient.processPayment(
                order.getId(),
                order.getTotalAmount()
            );
            
            // Шаг 3: Резервируем товары
            InventoryReservation reservation = inventoryClient.reserve(
                order.getId(),
                order.getItems()
            );
            
            // Шаг 4: Создаём отправку
            Shipment shipment = shippingClient.createShipment(
                order.getId(),
                order.getDeliveryAddress()
            );
            
            // Шаг 5: Обновляем статус
            order.setStatus(OrderStatus.CONFIRMED);
            order.setPaymentId(payment.getId());
            order.setReservationId(reservation.getId());
            order.setShipmentId(shipment.getId());
            
            return orderClient.updateOrder(order);
            
        } catch (PaymentException e) {
            handlePaymentFailure(order);
            throw e;
        } catch (InventoryException e) {
            handleInventoryFailure(order);
            throw e;
        }
    }
    
    private void handlePaymentFailure(Order order) {
        order.setStatus(OrderStatus.FAILED);
        orderClient.updateOrder(order);
    }
    
    private void handleInventoryFailure(Order order) {
        // Компенсирующая транзакция
        paymentClient.refund(order.getId());
        order.setStatus(OrderStatus.CANCELLED);
        orderClient.updateOrder(order);
    }
}

Подход 3: Использование Temporal Workflows

Для сложных, долгоживущих процессов использовал Temporal (на основе Cadence).

public interface OrderWorkflow {
    OrderResult processOrder(OrderRequest request);
}

@WorkflowImpl
public class OrderWorkflowImpl implements OrderWorkflow {
    
    private final OrderActivities activities = Workflow.newActivityStub(
        OrderActivities.class,
        ActivityOptions.newBuilder()
            .setStartToCloseTimeout(Duration.ofMinutes(10))
            .setRetryPolicy(defaultRetryPolicy())
            .build()
    );
    
    @Override
    public OrderResult processOrder(OrderRequest request) {
        try {
            // Шаг 1: Создать заказ
            OrderInfo order = activities.createOrder(request);
            
            // Шаг 2: Обработать платёж (с retry)
            PaymentResult payment = activities.processPayment(
                order.getId(),
                order.getAmount()
            );
            
            // Шаг 3: Зарезервировать товары
            InventoryResult inventory = activities.reserveInventory(
                order.getId(),
                request.getItems()
            );
            
            // Шаг 4: Создать отправку
            ShippingResult shipping = activities.createShipment(
                order.getId(),
                request.getDeliveryAddress()
            );
            
            return new OrderResult(OrderStatus.SUCCESS, order.getId());
            
        } catch (ApplicationFailure e) {
            // Компенсирующие транзакции
            activities.compensatePayment(request.getPaymentId());
            activities.compensateInventory(request.getOrderId());
            
            return new OrderResult(OrderStatus.FAILED, null);
        }
    }
}

@ActivityImpl
public class OrderActivitiesImpl implements OrderActivities {
    @Override
    public PaymentResult processPayment(String orderId, BigDecimal amount) {
        // Может быть переиспользована при перезапуске Workflow
        return paymentService.process(orderId, amount);
    }
}

Сравнение подходов

ПодходСложностьГибкостьОтказоустойчивость
Choreography (Events)СредняяВысокаяТребует компенсации
Orchestrator ServiceНизкаяСредняяХорошая
Temporal WorkflowsВысокаяВысокаяОтличная (встроена)

Критерии выбора

  1. Choreography — для простых, независимых процессов с несколькими сервисами
  2. Orchestrator — для средних по сложности флоу (3-4 сервиса)
  3. Temporal — для долгоживущих, сложных процессов с высокими требованиями к надёжности

Best Practices, которые мы применили

  • Idempotency keys — для предотвращения повторной обработки
  • Circuit Breaker — для защиты от каскадных отказов
  • Dead Letter Queues — для обработки ошибок
  • Event Sourcing — для полного аудита операций
  • Monitoring & Tracing — Prometheus + Jaeger для видимости
  • Compensation Logic — явная компенсация для откатов

Этот опыт показал, что выбор оркестрации зависит от специфики бизнес-процесса и требований к отказоустойчивости.

Как оркестрировался бизнес-флоу в микросервисах на последней работе | PrepBro