Skip to content

Instantly share code, notes, and snippets.

@alexandrebl
Created February 23, 2026 20:23
Show Gist options
  • Select an option

  • Save alexandrebl/f0611eb7b6a81b6aa535acbbbc9494c2 to your computer and use it in GitHub Desktop.

Select an option

Save alexandrebl/f0611eb7b6a81b6aa535acbbbc9494c2 to your computer and use it in GitHub Desktop.
// ========================================================================================================
// SISTEMA DE PROCESSAMENTO DE PEDIDOS COM AKKA.NET - PIPELINE DE ATORES ENCADEADOS
// ========================================================================================================
//
// Este projeto demonstra o uso de ATORES ENCADEADOS (Actor Pipeline) usando o framework Akka.NET.
// O objetivo é processar pedidos através de múltiplas etapas, onde cada ator é responsável por
// uma única responsabilidade (Single Responsibility Principle).
//
// --------------------------------------------------------------------------------------------------------
// O QUE É AKKA.NET?
// --------------------------------------------------------------------------------------------------------
// Akka.NET é um framework para construir sistemas concorrentes, distribuídos e resilientes usando
// o modelo de ATORES. Cada ator é uma unidade isolada de processamento que:
// - Possui seu próprio estado privado
// - Processa mensagens de forma assíncrona e sequencial
// - Comunica-se com outros atores apenas através de mensagens
// - Não compartilha memória (evita problemas de concorrência)
//
// --------------------------------------------------------------------------------------------------------
// ARQUITETURA DO PIPELINE DE PROCESSAMENTO
// --------------------------------------------------------------------------------------------------------
//
// O sistema implementa um PIPELINE DE 5 ATORES ENCADEADOS que processam pedidos em etapas:
//
// ┌─────────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐
// │ OrderReceiver │ ───> │ Validation │ ───> │ Payment │ ───> │ Shipping │ ───> │ Notification │
// │ Actor │ │ Actor │ │ Actor │ │ Actor │ │ Actor │
// └─────────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────────┘
// Etapa 1 Etapa 2 Etapa 3 Etapa 4 Etapa 5
//
// --------------------------------------------------------------------------------------------------------
// FLUXO DETALHADO DE PROCESSAMENTO
// --------------------------------------------------------------------------------------------------------
//
// 1. ORDERRECEIVER ACTOR (Recepção)
// - Recebe: Order (pedido original)
// - Função: Ponto de entrada do sistema, recebe pedidos dos clientes
// - Processa: Registra o recebimento do pedido
// - Envia para: ValidationActor
//
// 2. VALIDATION ACTOR (Validação)
// - Recebe: Order
// - Função: Valida os dados do pedido (quantidade > 0 e preço > 0)
// - Processa: Verifica integridade dos dados
// - Envia para: PaymentActor (se válido) ou interrompe o fluxo (se inválido)
// - Mensagem enviada: ValidatedOrder
//
// 3. PAYMENT ACTOR (Pagamento)
// - Recebe: ValidatedOrder
// - Função: Processa o pagamento do pedido
// - Processa: Calcula o valor total, simula aprovação do pagamento, gera ID de transação
// - Envia para: ShippingActor
// - Mensagem enviada: PaidOrder (inclui TransactionId)
//
// 4. SHIPPING ACTOR (Envio)
// - Recebe: PaidOrder
// - Função: Prepara e despacha o pedido
// - Processa: Simula preparação do envio, gera código de rastreamento
// - Envia para: NotificationActor
// - Mensagem enviada: ShippedOrder (inclui TrackingNumber)
//
// 5. NOTIFICATION ACTOR (Notificação)
// - Recebe: ShippedOrder
// - Função: Envia notificação final ao cliente
// - Processa: Gera notificação com todos os detalhes do pedido concluído
// - Final do pipeline: Não envia para outros atores
//
// --------------------------------------------------------------------------------------------------------
// TIPOS DE MENSAGENS NO PIPELINE
// --------------------------------------------------------------------------------------------------------
//
// As mensagens são objetos imutáveis que transportam dados entre atores:
//
// Order → Pedido original (Id, Product, Quantity, Price)
// ValidatedOrder → Pedido validado (Order + IsValid)
// PaidOrder → Pedido pago (Order + TransactionId)
// ShippedOrder → Pedido enviado (Order + TrackingNumber)
// CompletedOrder → Pedido concluído (Order + Status) [definida mas não usada neste exemplo]
//
// --------------------------------------------------------------------------------------------------------
// VANTAGENS DESTA ARQUITETURA
// --------------------------------------------------------------------------------------------------------
//
// 1. DESACOPLAMENTO: Cada ator conhece apenas o próximo ator na cadeia
// 2. ESCALABILIDADE: Pode-se adicionar múltiplas instâncias de cada ator para processar em paralelo
// 3. RESILIÊNCIA: Se um ator falha, o sistema pode reiniciá-lo sem afetar os outros
// 4. MANUTENIBILIDADE: Cada ator tem uma responsabilidade clara e bem definida
// 5. TESTABILIDADE: Cada ator pode ser testado isoladamente
// 6. ASSÍNCRONO: Processamento não-bloqueante, cada ator processa em seu próprio ritmo
//
// --------------------------------------------------------------------------------------------------------
// CONCEITOS IMPORTANTES DO AKKA.NET UTILIZADOS
// --------------------------------------------------------------------------------------------------------
//
// - ActorSystem: Container que gerencia todos os atores
// - ActorOf: Cria uma nova instância de ator
// - Props.Create: Factory para criar atores com parâmetros de construtor
// - IActorRef: Referência para enviar mensagens a um ator (não acessa o ator diretamente)
// - Tell(): Envia mensagem assíncrona (fire-and-forget)
// - Receive<T>: Define um handler SÍNCRONO para processar mensagens do tipo T
// - ReceiveAsync<T>: Define um handler ASSÍNCRONO para processar mensagens do tipo T (permite await)
// - ReceiveActor: Classe base para atores que usam pattern matching de mensagens
//
// --------------------------------------------------------------------------------------------------------
// ASYNC/AWAIT NO AKKA.NET - BOAS PRÁTICAS
// --------------------------------------------------------------------------------------------------------
//
// 1. Use ReceiveAsync<T> ao invés de Receive<T> quando precisar fazer operações assíncronas
// 2. Sempre aguarde (await) operações I/O: chamadas HTTP, banco de dados, leitura de arquivos
// 3. Use Task.Delay() para simular latência ou throttling de operações
// 4. Evite bloqueio com .Result ou .Wait() - sempre use await
// 5. Cada ator processa mensagens sequencialmente, então async/await não quebra o modelo de atores
// 6. O ator continua processando outras mensagens apenas após o await completar
//
// --------------------------------------------------------------------------------------------------------
using Akka.Actor;
// ========================================================================================================
// INICIALIZAÇÃO DO SISTEMA DE ATORES
// ========================================================================================================
// Cria o ActorSystem - container raiz que gerencia todos os atores
// Nome do sistema: "MyActorSystem" - identificador único do sistema
var actorSystem = ActorSystem.Create("MyActorSystem");
// ========================================================================================================
// CRIAÇÃO DO PIPELINE DE ATORES
// ========================================================================================================
// IMPORTANTE: Os atores são criados na ordem INVERSA do fluxo (do fim para o início)
// Isso é necessário porque cada ator precisa receber a referência (IActorRef) do próximo ator
// no seu construtor para poder enviar mensagens (Tell) para ele.
//
// Fluxo do pipeline: OrderReceiver → Validation → Payment → Shipping → Notification
// Ordem de criação: Notification ← Shipping ← Payment ← Validation ← OrderReceiver
// ========================================================================================================
// 5. Notification Actor - Final do pipeline (não precisa de referência para próximo ator)
var notificationActor = actorSystem.ActorOf<NotificationActor>("notification");
// 4. Shipping Actor - Recebe referência do NotificationActor
var shippingActor = actorSystem.ActorOf(Props.Create(() => new ShippingActor(notificationActor)), "shipping");
// 3. Payment Actor - Recebe referência do ShippingActor
var paymentActor = actorSystem.ActorOf(Props.Create(() => new PaymentActor(shippingActor)), "payment");
// 2. Validation Actor - Recebe referência do PaymentActor
var validationActor = actorSystem.ActorOf(Props.Create(() => new ValidationActor(paymentActor)), "validation");
// 1. Order Receiver Actor - Início do pipeline, recebe referência do ValidationActor
var orderReceiver = actorSystem.ActorOf(Props.Create(() => new OrderReceiverActor(validationActor)), "orderReceiver");
// ========================================================================================================
// ENVIO DE PEDIDOS PARA PROCESSAMENTO
// ========================================================================================================
// Tell() é um método "fire-and-forget" (envia e esquece)
// As mensagens são processadas assincronamente pelos atores
// ========================================================================================================
orderReceiver.Tell(new Order { Id = 1, Product = "Notebook", Quantity = 1, Price = 3500.00m });
orderReceiver.Tell(new Order { Id = 2, Product = "Mouse", Quantity = 2, Price = 50.00m });
orderReceiver.Tell(new Order { Id = 3, Product = "Teclado", Quantity = 1, Price = 250.00m });
Console.WriteLine("Aguardando processamento dos pedidos...\n");
// ========================================================================================================
// MONITORAMENTO DO SISTEMA
// ========================================================================================================
// Usa CancellationToken para controle cooperativo do loop de monitoramento
// Substitui o uso de Timer tradicional por abordagem totalmente assíncrona
// ========================================================================================================
// Loop assíncrono com Task.Delay ao invés de Timer (boa prática async/await)
var cancellationTokenSource = new CancellationTokenSource();
var monitoringTask = MonitorSystemAsync(cancellationTokenSource.Token);
// Aguarda 10 segundos de forma assíncrona (permite que o pipeline processe os pedidos)
await Task.Delay(TimeSpan.FromSeconds(10));
// Cancela o loop de monitoramento de forma cooperativa
cancellationTokenSource.Cancel();
try
{
// Aguarda o término gracioso do loop de monitoramento
await monitoringTask;
}
catch (OperationCanceledException)
{
// Exceção esperada quando cancelamos o token - comportamento normal
}
// ========================================================================================================
// ENCERRAMENTO DO SISTEMA DE ATORES
// ========================================================================================================
// Terminate() encerra todos os atores de forma ordenada e libera recursos
// É uma operação assíncrona que aguarda todos os atores finalizarem
// ========================================================================================================
Console.WriteLine("\nEncerrando o sistema de atores...");
await actorSystem.Terminate();
// ========================================================================================================
// MÉTODO DE MONITORAMENTO DO SISTEMA
// ========================================================================================================
// Este método executa um loop assíncrono para monitorar a saúde do sistema de atores.
// Em um sistema de produção, este método poderia:
// - Verificar métricas de performance dos atores
// - Monitorar filas de mensagens
// - Registrar estatísticas em logs
// - Enviar alertas se houver problemas
//
// Usa CancellationToken para permitir cancelamento cooperativo e gracioso do loop.
// ========================================================================================================
static async Task MonitorSystemAsync(CancellationToken cancellationToken)
{
int count = 0;
while (!cancellationToken.IsCancellationRequested)
{
try
{
// Aguarda de forma assíncrona ao invés de usar Timer (boa prática async/await)
await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken);
count++;
Console.WriteLine($"\n[Monitor] Tick: {count} - Sistema em execução...");
}
catch (OperationCanceledException)
{
// Operação cancelada é esperada quando o token é cancelado
// Permite saída limpa do loop
break;
}
}
}
// ========================================================================================================
// DEFINIÇÃO DAS MENSAGENS DO PIPELINE
// ========================================================================================================
// As mensagens são objetos IMUTÁVEIS que transportam dados entre atores.
// Seguindo o padrão de Akka.NET, as mensagens devem ser:
// - Imutáveis (readonly ou init-only properties quando possível)
// - Serializáveis (para permitir distribuição remota)
// - Pequenas e focadas (cada mensagem representa um evento ou comando específico)
// ========================================================================================================
/// <summary>
/// Mensagem inicial que representa um pedido recebido do cliente.
/// Esta é a mensagem de entrada do pipeline.
/// </summary>
public class Order
{
public int Id { get; set; }
public string Product { get; set; } = string.Empty;
public int Quantity { get; set; }
public decimal Price { get; set; }
}
/// <summary>
/// Mensagem que transporta um pedido validado.
/// Contém o pedido original e o resultado da validação.
/// </summary>
public class ValidatedOrder
{
public Order Order { get; set; } = null!;
public bool IsValid { get; set; }
}
/// <summary>
/// Mensagem que transporta um pedido pago.
/// Inclui o ID da transação gerado pelo gateway de pagamento.
/// </summary>
public class PaidOrder
{
public Order Order { get; set; } = null!;
public string TransactionId { get; set; } = string.Empty;
}
/// <summary>
/// Mensagem que transporta um pedido enviado.
/// Inclui o código de rastreamento da transportadora.
/// </summary>
public class ShippedOrder
{
public Order Order { get; set; } = null!;
public string TrackingNumber { get; set; } = string.Empty;
}
/// <summary>
/// Mensagem que representa um pedido completamente processado.
/// Reservada para uso futuro em extensões do pipeline.
/// </summary>
public class CompletedOrder
{
public Order Order { get; set; } = null!;
public string Status { get; set; } = string.Empty;
}
// ========================================================================================================
// IMPLEMENTAÇÃO DOS ATORES DO PIPELINE
// ========================================================================================================
// Cada ator herda de ReceiveActor e usa ReceiveAsync<T> para processar mensagens de forma assíncrona.
// Os atores são independentes e se comunicam APENAS através de mensagens (Tell).
// ========================================================================================================
// ========================================================================================================
// IMPLEMENTAÇÃO DOS ATORES DO PIPELINE
// ========================================================================================================
// Cada ator herda de ReceiveActor e usa ReceiveAsync<T> para processar mensagens de forma assíncrona.
// Os atores são independentes e se comunicam APENAS através de mensagens (Tell).
// ========================================================================================================
/// <summary>
/// ATOR 1: ORDER RECEIVER ACTOR (Ponto de Entrada do Sistema)
///
/// Responsabilidades:
/// - Receber pedidos de clientes externos
/// - Persistir pedidos no banco de dados
/// - Encaminhar pedidos para validação
///
/// Integrações Simuladas:
/// - Banco de dados (persistência de pedidos)
/// </summary>
public class OrderReceiverActor : ReceiveActor
{
private readonly IActorRef _validationActor;
public OrderReceiverActor(IActorRef validationActor)
{
_validationActor = validationActor;
// ReceiveAsync permite usar await dentro do handler
ReceiveAsync<Order>(async order =>
{
Console.WriteLine($"[OrderReceiver] Pedido #{order.Id} recebido: {order.Product} x{order.Quantity}");
// Simula operação assíncrona: salvar pedido no banco de dados
// Em produção: await dbContext.Orders.AddAsync(order); await dbContext.SaveChangesAsync();
await Task.Delay(100); // Simula latência de I/O do banco (~100ms)
Console.WriteLine($"[OrderReceiver] Pedido #{order.Id} salvo no banco de dados");
// Envia para o próximo ator na cadeia (desacoplamento via mensagens)
_validationActor.Tell(order);
});
}
}
/// <summary>
/// ATOR 2: VALIDATION ACTOR (Validador de Dados e Regras de Negócio)
///
/// Responsabilidades:
/// - Validar integridade dos dados do pedido
/// - Verificar disponibilidade de estoque
/// - Aplicar regras de negócio (limites, restrições)
/// - Decidir se o pedido pode prosseguir no pipeline
///
/// Integrações Simuladas:
/// - API de validação de regras de negócio
/// - Serviço de verificação de estoque
/// </summary>
public class ValidationActor : ReceiveActor
{
private readonly IActorRef _paymentActor;
public ValidationActor(IActorRef paymentActor)
{
_paymentActor = paymentActor;
// ReceiveAsync para operações assíncronas de validação
ReceiveAsync<Order>(async order =>
{
Console.WriteLine($"[Validation] Validando pedido #{order.Id}...");
// Simula validação assíncrona: verificar estoque, consultar regras de negócio via API
// Em produção: await stockService.CheckAvailabilityAsync(order.Product, order.Quantity);
await Task.Delay(200); // Simula latência de chamada a serviço externo (~200ms)
// Aplica regras de validação
bool isValid = order.Quantity > 0 && order.Price > 0;
if (isValid)
{
Console.WriteLine($"[Validation] Pedido #{order.Id} validado com sucesso!");
// Encapsula o pedido em ValidatedOrder e envia para próximo ator
_paymentActor.Tell(new ValidatedOrder { Order = order, IsValid = true });
}
else
{
Console.WriteLine($"[Validation] Pedido #{order.Id} inválido!");
// Pipeline interrompido - pedido não avança para pagamento
}
});
}
}
/// <summary>
/// ATOR 3: PAYMENT ACTOR (Processador de Pagamentos)
///
/// Responsabilidades:
/// - Calcular valor total do pedido
/// - Processar pagamento via gateway externo
/// - Gerar e armazenar ID de transação
/// - Encaminhar pedidos pagos para envio
///
/// Integrações Simuladas:
/// - Gateway de pagamento (Stripe, PayPal, PagSeguro, etc)
/// </summary>
public class PaymentActor : ReceiveActor
{
private readonly IActorRef _shippingActor;
public PaymentActor(IActorRef shippingActor)
{
_shippingActor = shippingActor;
// ReceiveAsync para processar pagamento de forma assíncrona
ReceiveAsync<ValidatedOrder>(async validatedOrder =>
{
// Só processa pedidos que passaram pela validação
if (validatedOrder.IsValid)
{
var order = validatedOrder.Order;
decimal totalAmount = order.Price * order.Quantity;
Console.WriteLine($"[Payment] Processando pagamento de R$ {totalAmount:F2} para pedido #{order.Id}...");
// Chama método auxiliar para simular integração com gateway de pagamento
await ProcessPaymentAsync(order.Id, totalAmount);
// Gera ID único de transação (em produção, viria do gateway)
string transactionId = Guid.NewGuid().ToString()[..8];
Console.WriteLine($"[Payment] Pagamento aprovado! Transaction ID: {transactionId}");
// Encapsula pedido pago e envia para o próximo ator
_shippingActor.Tell(new PaidOrder { Order = order, TransactionId = transactionId });
}
});
}
/// <summary>
/// Simula chamada assíncrona a um gateway de pagamento externo.
/// Em produção, este método faria requisição HTTP a APIs como:
/// - Stripe: await stripe.Charges.CreateAsync(chargeOptions)
/// - PayPal: await paypalClient.CreatePaymentAsync(payment)
/// - Mercado Pago: await mercadoPagoClient.ProcessPaymentAsync(request)
/// </summary>
private static async Task ProcessPaymentAsync(int orderId, decimal amount)
{
// Simula latência de rede ao chamar API externa
// 300-500ms é realista para APIs de pagamento
await Task.Delay(350);
// Aqui seria feita a chamada real à API do gateway de pagamento
// Exemplo: var result = await httpClient.PostAsync("https://api.payment-gateway.com/charge", content);
}
}
/// <summary>
/// ATOR 4: SHIPPING ACTOR (Gerenciador de Envio e Logística)
///
/// Responsabilidades:
/// - Integrar com APIs de transportadoras
/// - Gerar etiquetas de envio
/// - Obter códigos de rastreamento
/// - Encaminhar pedidos despachados para notificação
///
/// Integrações Simuladas:
/// - API de transportadoras (Correios, FedEx, UPS, DHL, etc)
/// </summary>
public class ShippingActor : ReceiveActor
{
private readonly IActorRef _notificationActor;
public ShippingActor(IActorRef notificationActor)
{
_notificationActor = notificationActor;
// ReceiveAsync para processar envio de forma assíncrona
ReceiveAsync<PaidOrder>(async paidOrder =>
{
var order = paidOrder.Order;
Console.WriteLine($"[Shipping] Preparando envio do pedido #{order.Id}...");
// Chama método auxiliar para simular integração com API de transportadora
string trackingNumber = await GenerateShippingLabelAsync(order.Id, order.Product);
Console.WriteLine($"[Shipping] Pedido #{order.Id} despachado! Código de rastreio: {trackingNumber}");
// Encapsula pedido enviado e envia para o último ator (notificação)
_notificationActor.Tell(new ShippedOrder
{
Order = order,
TrackingNumber = trackingNumber
});
});
}
/// <summary>
/// Simula geração assíncrona de etiqueta de envio via API de transportadora.
/// Em produção, este método faria requisição HTTP a APIs como:
/// - Correios: await correiosClient.CreateShippingLabelAsync(address, dimensions)
/// - FedEx: await fedexClient.CreateShipmentAsync(shipmentRequest)
/// - Melhor Envio: await melhorEnvioClient.GenerateLabelAsync(order)
/// </summary>
private static async Task<string> GenerateShippingLabelAsync(int orderId, string product)
{
// Simula chamada assíncrona à API da transportadora (~250ms)
await Task.Delay(250);
// Aqui seria feita a chamada real à API da transportadora
// Exemplo: var response = await httpClient.PostAsync("https://api.shipping.com/create-label", content);
// Retornaria: response.TrackingNumber
// Gera código de rastreio simulado no formato brasileiro
return $"BR{Random.Shared.Next(10000000, 99999999)}";
}
}
/// <summary>
/// ATOR 5: NOTIFICATION ACTOR (Gerenciador de Notificações Multi-Canal)
///
/// Responsabilidades:
/// - Enviar notificações por email
/// - Enviar notificações por SMS
/// - Enviar push notifications (opcional)
/// - Registrar conclusão do processamento do pedido
///
/// Integrações Simuladas:
/// - Serviço de email (SendGrid, AWS SES, SMTP)
/// - Serviço de SMS (Twilio, AWS SNS)
///
/// Este é o ÚLTIMO ator do pipeline - finaliza o processamento.
/// </summary>
public class NotificationActor : ReceiveActor
{
public NotificationActor()
{
// ReceiveAsync para enviar notificações de forma assíncrona
ReceiveAsync<ShippedOrder>(async shippedOrder =>
{
var order = shippedOrder.Order;
Console.WriteLine($"[Notification] Enviando notificação para pedido #{order.Id}...");
// Envia notificações em paralelo para otimizar tempo
// Cada canal de notificação é independente
await SendEmailNotificationAsync(order.Id, order.Product, shippedOrder.TrackingNumber);
await SendSmsNotificationAsync(order.Id);
// Exibe resumo final do processamento
Console.WriteLine($"[Notification] ✓ Pedido #{order.Id} concluído com sucesso!");
Console.WriteLine($"[Notification] Produto: {order.Product}");
Console.WriteLine($"[Notification] Rastreio: {shippedOrder.TrackingNumber}");
// Pipeline completo: Order → Validation → Payment → Shipping → Notification ✓
});
}
/// <summary>
/// Simula envio assíncrono de notificação por email.
/// Em produção, este método integraria com serviços como:
/// - SendGrid: await sendGridClient.SendEmailAsync(message)
/// - AWS SES: await sesClient.SendEmailAsync(request)
/// - MailKit: await smtpClient.SendAsync(mimeMessage)
/// </summary>
private static async Task SendEmailNotificationAsync(int orderId, string product, string trackingNumber)
{
// Simula chamada assíncrona a serviço de email (~150ms)
await Task.Delay(150);
// Aqui seria feita a chamada real ao serviço de email
// Exemplo:
// var message = new EmailMessage {
// To = customer.Email,
// Subject = $"Pedido #{orderId} enviado!",
// Body = $"Seu produto {product} foi enviado. Rastreie com: {trackingNumber}"
// };
// await emailService.SendAsync(message);
}
/// <summary>
/// Simula envio assíncrono de notificação por SMS.
/// Em produção, este método integraria com serviços como:
/// - Twilio: await twilioClient.Messages.CreateAsync(messageOptions)
/// - AWS SNS: await snsClient.PublishAsync(publishRequest)
/// - Nexmo: await nexmoClient.SMS.SendAnSMSAsync(smsRequest)
/// </summary>
private static async Task SendSmsNotificationAsync(int orderId)
{
// Simula chamada assíncrona a serviço de SMS (~100ms)
await Task.Delay(100);
// Aqui seria feita a chamada real ao serviço de SMS
// Exemplo:
// await smsService.SendAsync(
// phoneNumber: customer.Phone,
// message: $"Pedido #{orderId} enviado! Verifique seu email para detalhes."
// );
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment