Microsserviços em C#: arquitetura e prática de nível avançado
A transição de um monólito para uma arquitetura de microsserviços na plataforma .NET não é apenas uma decisão técnica, mas um passo estratégico em direção à escalabilidade e resiliência. No entanto, em nível avançado, os desenvolvedores enfrentam problemas não triviais: transações distribuídas, consistência de dados e orquestração complexa. Neste artigo, vamos analisar os padrões arquiteturais que ajudam a construir um ecossistema de microsserviços confiável em C# e examinar exemplos práticos de código.
1. Padrão Saga: gerenciamento de transações distribuídas
Em microsserviços, transações ACID tradicionais são impossíveis. O padrão Saga resolve o problema de consistência de dados dividindo a operação global em uma sequência de transações locais com ações compensatórias.
Coreografia vs. Orquestração:
- Coreografia — cada serviço publica eventos e reage a eles. Adequado para cenários simples.
- Orquestração — um coordenador central (Orchestrator) gerencia as etapas. Recomendado para processos de negócios complexos.
Exemplo de implementação de orquestrador em C# usando MassTransit:
public class OrderSaga : MassTransitStateMachine<OrderSagaState>{ public State Submitted { get; set; } public State PaymentPending { get; set; } public State Completed { get; set; }
public Event<OrderSubmitted> OrderSubmitted { get; set; } public Event<PaymentProcessed> PaymentProcessed { get; set; } public Event<PaymentFailed> PaymentFailed { get; set; }
public OrderSaga() { InstanceState(x => x.CurrentState);
Initially( When(OrderSubmitted) .Then(context => context.Saga.OrderId = context.Message.OrderId) .TransitionTo(Submitted) .Publish(context => new ProcessPayment { OrderId = context.Saga.OrderId, Amount = context.Message.Amount }) );
During(Submitted, When(PaymentProcessed) .TransitionTo(Completed) .Finalize(), When(PaymentFailed) .Then(context => Console.WriteLine("Compensação: cancelamento do pedido")) .TransitionTo(Final) ); }}Este código demonstra como o orquestrador gerencia o ciclo de vida do pedido: com pagamento bem-sucedido, a saga é concluída; em caso de erro, a compensação é iniciada.
2. CQRS e Event Sourcing: separação de responsabilidades
O padrão CQRS (Command Query Responsibility Segregation) separa comandos (escrita) e consultas (leitura) em modelos diferentes. Combinado com Event Sourcing (ES), ele fornece auditoria completa e flexibilidade.
Arquitetura CQRS+ES:
- Comandos — alteram o estado por meio de eventos.
- Eventos — armazenados no Event Store (ex.: EventStoreDB).
- Projeções — constroem modelos de leitura para consultas rápidas.
Exemplo de comando e manipulador:
public class CreateOrderCommand : ICommand{ public Guid OrderId { get; set; } public decimal Amount { get; set; }}
public class CreateOrderHandler : ICommandHandler<CreateOrderCommand>{ private readonly IEventStore _eventStore;
public async Task Handle(CreateOrderCommand command) { var orderCreated = new OrderCreatedEvent { OrderId = command.OrderId, Amount = command.Amount, Timestamp = DateTime.UtcNow };
await _eventStore.AppendEventAsync( $"order-{command.OrderId}", orderCreated ); }