O Mundo é Assíncrono e Orientado a Eventos

Principais Pontos
  • Sistemas síncronos não refletem como o mundo real funciona - ninguém fica esperando na linha enquanto processos longos acontecem, mas nossos sistemas frequentemente fazem exatamente isso
  • Event Sourcing armazena toda a sequência de eventos em vez de apenas o estado atual, permitindo reconstruir qualquer momento do passado e criar múltiplas projeções dos mesmos dados
  • CQRS separa operações de escrita das de leitura, otimizando cada lado independentemente e permitindo escalabilidade seletiva baseada no uso real
  • Sagas coordenam processos distribuídos entre múltiplos serviços com capacidade de compensação automática quando algo falha no meio do caminho
  • Empresas que adotam arquitetura orientada a eventos ganham vantagens competitivas em flexibilidade, escalabilidade, resiliência e capacidade de personalização baseada em histórico completo

É segunda-feira de manhã. Você deixa seu celular na assistência técnica e sai para trabalhar. Três horas depois, recebe uma mensagem: "Seu aparelho está pronto para retirada." Simples, não é? Você não ficou esperando na loja por três horas. Não ligou a cada 10 minutos perguntando se estava pronto. Você simplesmente... foi notificado quando o evento relevante aconteceu.

Esta é a essência de um mundo orientado a eventos — e é exatamente assim que a maioria dos processos do mundo real funcionam. Então por que insistimos em construir sistemas que funcionam como se você tivesse que ficar esperando na fila da assistência técnica?

A Realidade Inconveniente dos Sistemas Síncronos

Imagine se o mundo funcionasse de forma síncrona:

  • Você liga para o restaurante para pedir comida e fica na linha por 45 minutos esperando o prato ficar pronto
  • Você vai ao médico e precisa esperar na sala de exame até todos os resultados de exames estarem prontos
  • Você envia um email e seu programa trava até a pessoa responder

Parece ridículo, não é? Mas é exatamente assim que a maioria dos nossos sistemas trabalha — fazendo requisições síncronas e bloqueantes, esperando respostas que podem nunca chegar ou demorar mais do que esperamos.

O Que São Eventos

Um evento é uma ocorrência significativa, um fato relevante que pode ser considerado digno de atenção. É algo que aconteceu no passado e que tem impacto no estado do nosso sistema.

Pense nisso como frames de um filme. Um único frame (o estado atual) te diz muito pouco sobre a história. Mas uma sequência de frames (eventos) conta uma narrativa completa:

// Estado atual - informação limitada
data class Usuario(
    val id: String,
    val nome: String,
    val plano: String = "premium"
)

// Sequência de eventos - história completa
sealed class EventoUsuario {
    data class UsuarioCriado(val id: String, val nome: String) : EventoUsuario()
    data class PlanoAlterado(val id: String, val planoAnterior: String, val novoPlano: String) : EventoUsuario()
    data class LoginRealizado(val id: String, val timestamp: Long) : EventoUsuario()
    data class CompraEfetuada(val id: String, val valor: Double, val produto: String) : EventoUsuario()
}

O estado atual te diz que o usuário tem plano premium. Os eventos te contam que ele começou no plano básico, fez upgrade depois de duas semanas, fez três compras no último mês, e tem um padrão de login nas manhãs de segunda-feira.

Construindo Sistemas que Refletem a Realidade

Event Sourcing

Em vez de armazenar apenas o estado atual, event sourcing armazena todos os eventos que levaram a esse estado:

class ContaBancaria(private val eventos: MutableList<EventoConta> = mutableListOf()) {
    
    fun depositar(valor: Double) {
        val evento = DepositoRealizado(valor, System.currentTimeMillis())
        eventos.add(evento)
        // Publique o evento para outros sistemas interessados
        eventBus.publish(evento)
    }
    
    fun sacar(valor: Double) {
        require(saldoAtual() >= valor) { "Saldo insuficiente" }
        val evento = SaqueRealizado(valor, System.currentTimeMillis())
        eventos.add(evento)
        eventBus.publish(evento)
    }
    
    fun saldoAtual(): Double {
        return eventos.filterIsInstance<DepositoRealizado>().sumOf { it.valor } -
               eventos.filterIsInstance<SaqueRealizado>().sumOf { it.valor }
    }
    
    fun historico(): List<EventoConta> = eventos.toList()
}
Vantagem Competitiva
Com event sourcing, você pode reconstruir o estado de qualquer momento no passado, fazer auditoria completa das mudanças, e criar projeções diferentes dos mesmos dados para diferentes propósitos.

Processamento Assíncrono

Aqui está onde a mágica realmente acontece. Em vez de processar tudo imediatamente, você reage aos eventos conforme eles acontecem:

class ProcessadorEventos {
    
    fun processar(evento: EventoUsuario) {
        when (evento) {
            is UsuarioCriado -> {
                // Processos que podem ser assíncronos
                enviarEmailBoasVindas(evento.id)
                criarPerfilRecomendacoes(evento.id)
                adicionarMetricas("usuario_criado")
            }
            
            is CompraEfetuada -> {
                // Múltiplos sistemas podem reagir ao mesmo evento
                atualizarInventario(evento.produto)
                calcularComissaoVendedor(evento.valor)
                dispararRecomendacoes(evento.id)
                atualizarAnalytics(evento)
            }
        }
    }
    
    private suspend fun enviarEmailBoasVindas(usuarioId: String) {
        // Operação assíncrona que não bloqueia o fluxo principal
        coroutineScope.launch {
            val template = buscarTemplate("boas-vindas")
            val usuario = buscarUsuario(usuarioId)
            emailService.enviar(usuario.email, template)
        }
    }
}

Message Brokers

Para sistemas maiores, você precisa de infraestrutura dedicada para gerenciar eventos:

class EventBus {
    private val subscribers = mutableMapOf<String, MutableList<(Any) -> Unit>>()
    
    inline fun <reified T> subscribe(crossinline handler: (T) -> Unit) {
        val eventType = T::class.simpleName!!
        subscribers.getOrPut(eventType) { mutableListOf() }
            .add { event -> if (event is T) handler(event) }
    }
    
    fun publish(event: Any) {
        val eventType = event::class.simpleName!!
        subscribers[eventType]?.forEach { handler ->
            // Processamento assíncrono para não bloquear o publisher
            coroutineScope.launch {
                try {
                    handler(event)
                } catch (e: Exception) {
                    // Log e recuperação de erros sem falhar o sistema inteiro
                    logger.error("Erro processando evento $eventType", e)
                }
            }
        }
    }
}

Padrões Avançados

CQRS

Separe as operações de escrita (commands) das de leitura (queries):

// Command Side - otimizado para escritas
class PedidoCommandHandler {
    fun criarPedido(command: CriarPedido): Result<String> {
        return try {
            val evento = PedidoCriado(
                id = UUID.randomUUID().toString(),
                usuarioId = command.usuarioId,
                items = command.items,
                timestamp = System.currentTimeMillis()
            )
            
            eventStore.append(evento)
            eventBus.publish(evento)
            Result.success(evento.id)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }
}

// Query Side - otimizado para leituras
class PedidoQueryHandler {
    private val projecoes = mutableMapOf<String, PedidoProjection>()
    
    init {
        // Escuta eventos e constrói projeções otimizadas para queries
        eventBus.subscribe<PedidoCriado> { evento ->
            projecoes[evento.id] = PedidoProjection(
                id = evento.id,
                usuario = buscarNomeUsuario(evento.usuarioId),
                valorTotal = evento.items.sumOf { it.preco },
                status = "criado"
            )
        }
    }
    
    fun buscarPedido(id: String): PedidoProjection? = projecoes[id]
}

Sagas

Para processos que envolvem múltiplos serviços:

class ProcessamentoPedidoSaga {
    
    suspend fun processar(pedidoCriado: PedidoCriado) {
        try {
            // Passo 1: Reservar estoque
            val estoqueReservado = estoqueService.reservar(pedidoCriado.items)
            
            // Passo 2: Processar pagamento
            val pagamentoProcessado = pagamentoService.processar(
                pedidoCriado.usuarioId, 
                pedidoCriado.valorTotal
            )
            
            // Passo 3: Confirmar pedido
            eventBus.publish(PedidoConfirmado(pedidoCriado.id))
            
        } catch (e: Exception) {
            // Compensação: desfazer operações já realizadas
            compensar(pedidoCriado.id)
            eventBus.publish(PedidoFalhou(pedidoCriado.id, e.message))
        }
    }
    
    private suspend fun compensar(pedidoId: String) {
        // Reverter reserva de estoque, cancelar pagamento, etc.
        estoqueService.cancelarReserva(pedidoId)
        pagamentoService.cancelar(pedidoId)
    }
}

O Poder da Personalização e Contexto

Com arquitetura orientada a eventos, você pode construir sistemas que realmente entendem seus usuários:

Exemplo Real
Imagine um e-commerce que sabe que você sempre compra ração para gato na primeira semana do mês. Em vez de bombardear você com ofertas aleatórias, ele pode te mandar uma notificação discreta quando você está chegando no fim da ração, baseado no seu histórico de compras.

Isso só é possível porque o sistema mantém o histórico completo de eventos, não apenas o estado atual do seu carrinho.

Implementação Prática

Você não precisa refatorar todo seu sistema de uma vez. Comece identificando um fluxo específico:

// Começe com algo simples como notificações
class NotificacaoService {
    
    init {
        eventBus.subscribe<UsuarioLogou> { evento ->
            // Se o usuário não fez login por mais de 7 dias
            if (diasSemLogin(evento.usuarioId) > 7) {
                enviarNotificacao(evento.usuarioId, "Sentimos sua falta!")
            }
        }
        
        eventBus.subscribe<CompraEfetuada> { evento ->
            // Personalização baseada no histórico
            val recomendacoes = gerarRecomendacoes(evento.usuarioId, evento.produto)
            enviarEmail(evento.usuarioId, "Produtos similares", recomendacoes)
        }
    }
}

Por Que Investir Agora?

As empresas que estão investindo em arquitetura orientada a eventos hoje têm uma vantagem competitiva significativa:

  1. Flexibilidade: Novos recursos podem se conectar aos eventos existentes sem modificar código legado
  2. Escalabilidade: Componentes podem ser escalados independentemente
  3. Resiliência: Falhas em um componente não derrubam o sistema inteiro
  4. Auditoria: Histórico completo de tudo que aconteceu
  5. Inteligência: Dados para machine learning e personalização

Ferramentas e Tecnologias

Para implementar arquitetura orientada a eventos, considere:

  • Message Brokers: Apache Kafka, RabbitMQ, AWS SQS
  • Event Stores: EventStore, Apache Kafka, AWS DynamoDB
  • Stream Processing: Apache Kafka Streams, Apache Flink
  • Bibliotecas: Axon Framework (Java), EventFlow (.NET), Commanded (Elixir)

Conclusão

O mundo real é assíncrono. As pessoas não ficam paradas esperando as coisas acontecerem — elas reagem quando eventos relevantes ocorrem. Nossos sistemas deveriam funcionar da mesma forma.

Não é suficiente ter apenas a "foto" do estado atual. Você precisa do "filme" completo — a sequência de eventos que conta a história de como você chegou até aqui. Essa história é onde está o verdadeiro valor: na capacidade de entender padrões, prever comportamentos e criar experiências verdadeiramente personalizadas.

A pergunta não é se você deveria adotar arquitetura orientada a eventos, mas quando. E para muitas empresas, esse momento é agora — antes que seus concorrentes descubram a vantagem competitiva que essa abordagem oferece.

Como diria um sábio desenvolvedor: "O futuro pertence àqueles que conseguem ver não apenas onde estão, mas também o caminho que percorreram para chegar lá."