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

Приведи пример решения архитектурной проблемы

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

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

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

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

Пример решения архитектурной проблемы: Управление состоянием в распределённом приложении

Рассмотрим классическую архитектурную проблему в C# Backend: синхронизация состояния между несколькими микросервисами при обработке заказов. В традиционной монолитной системе состояние заказа легко управляется в единой базе данных. Однако в распределённой микросервисной архитектуре каждый сервис (OrderService, PaymentService, InventoryService) имеет свою собственную базу данных и должен знать о изменениях состояния, происходящих в других сервисах.

Проблема

Изначальная архитектура использовала прямой REST API вызов между сервисами:

  • OrderService создаёт заказ и вызывает PaymentService для обработки платежа.
  • PaymentService после успешной оплаты вызывает InventoryService для резервирования товара.
  • Если InventoryService недоступен, состояние заказа становится неконсистентным.

Это приводит к:

  1. Жёсткой связности между сервисами
  2. Проблемам с отказоустойчивостью (цепочка вызовов может оборваться)
  3. Сложности масштабирования каждого сервиса независимо
  4. Невозможности отслеживания полного workflow заказа

Решение через Event-Driven Architecture и Saga Pattern

Мы переходим от синхронной коммуникации к асинхронной Event-Driven Architecture с использованием Saga Pattern для управления distributed transactions.

Ключевые компоненты решения:

  1. Message Broker (RabbitMQ/Kafka) для асинхронной коммуникации
  2. Orchestration Saga для координации workflow
  3. Event Sourcing для отслеживания изменений состояния

Пример реализации Saga Orchestrator в C#

// Saga Orchestrator для обработки заказа
public class OrderProcessingSaga
{
    private readonly IMessageBus _messageBus;
    private readonly SagaStateRepository _repository;
    
    public OrderProcessingSaga(IMessageBus messageBus, SagaStateRepository repository)
    {
        _messageBus = messageBus;
        _repository = repository;
    }
    
    public async Task StartSaga(OrderCreatedEvent orderEvent)
    {
        var sagaId = Guid.NewGuid();
        var sagaState = new OrderSagaState(sagaId, orderEvent.OrderId);
        
        // Сохраняем начальное состояние Saga
        await _repository.Save(sagaState);
        
        // Публикуем событие для начала обработки платежа
        await _messageBus.Publish(new ProcessPaymentCommand 
        {
            SagaId = sagaId,
            OrderId = orderEvent.OrderId,
            Amount = orderEvent.TotalAmount
        });
    }
    
    public async Task Handle(PaymentCompletedEvent paymentEvent)
    {
        var sagaState = await _repository.Get(paymentEvent.SagaId);
        sagaState.PaymentCompleted = true;
        
        await _repository.Save(sagaState);
        
        // Публикуем событие для резервирования товара
        await _messageBus.Publish(new ReserveInventoryCommand
        {
            SagaId = paymentEvent.SagaId,
            OrderId = paymentEvent.OrderId,
            Items = sagaState.OrderItems
        });
    }
    
    public async Task Handle(InventoryReservedEvent inventoryEvent)
    {
        var sagaState = await _repository.Get(inventoryEvent.SagaId);
        sagaState.InventoryReserved = true;
        sagaState.Status = OrderStatus.ProcessingCompleted;
        
        await _repository.Save(sagaState);
        
        // Публикуем финальное событие завершения Saga
        await _messageBus.Publish(new OrderProcessingCompletedEvent
        {
            OrderId = inventoryEvent.OrderId,
            SagaId = inventoryEvent.SagaId
        });
    }
    
    // Обработка компенсирующих транзакций при ошибках
    public async Task Handle(PaymentFailedEvent failedEvent)
    {
        var sagaState = await _repository.Get(failedEvent.SagaId);
        sagaState.Status = OrderStatus.Cancelled;
        
        await _repository.Save(sagaState);
        
        // Публикуем события отката для всех сервисов
        await _messageBus.Publish(new CancelOrderCommand
        {
            OrderId = failedEvent.OrderId,
            Reason = "Payment failed"
        });
    }
}

Структура Saga State для отслеживания прогресса

public class OrderSagaState
{
    public Guid SagaId { get; set; }
    public Guid OrderId { get; set; }
    public bool PaymentCompleted { get; set; }
    public bool InventoryReserved { get; set; }
    public OrderStatus Status { get; set; }
    public List<OrderItem> OrderItems { get; set; }
    
    public OrderSagaState(Guid sagaId, Guid orderId)
    {
        SagaId = sagaId;
        OrderId = orderId;
        Status = OrderStatus.InProgress;
        PaymentCompleted = false;
        InventoryReserved = false;
    }
}

Преимущества нового архитектурного решения:

  • Снижение связности: Сервисы взаимодействуют только через события, не знают о существовании друг друга напрямую
  • Улучшенная отказоустойчивость: Saga Orchestrator может восстановить состояние после сбоев любого сервиса
  • Возможность компенсирующих транзакций: При ошибках в любом шаге можно выполнить откат изменений
  • Лёгкое масштабирование: Каждый сервис может масштабироваться независимо
  • Полная трассировка workflow: Saga State хранит всю историю обработки заказа
  • Поддержка eventual consistency: Система достигает консистентности через некоторое время, что приемлемо для бизнес-процессов

Архитектурные принципы, применённые в решении:

  1. Принцип единственной ответственности: Каждый сервис отвечает только за свою область
  2. Принцип асинхронной коммуникации: Сервисы не блокируют друг друга
  3. Принцип компенсирующих действий: Возможность отката распределённой транзакции
  4. Принцип отслеживаемости: Полная история состояния через Saga State

Это решение демонстрирует переход от синхронной, tightly-coupled архитектуры к асинхронной, event-driven архитектуре, что является стандартным подходом для сложных распределённых систем в современном C# Backend разработке.

Приведи пример решения архитектурной проблемы | PrepBro