Arquitetura orientada a eventos é um estilo de design em que serviços anunciam fatos imutáveis como eventos e consumidores reagem de forma independente. A precisão que as páginas de fornecedores pulam mora em duas palavras: um evento é um fato imutável, no passado — OrderPlaced, não PlaceOrder — e o produtor não sabe nem se importa com quem reage. Essa indiferença é a arquitetura: consumidores são adicionados, removidos e quebram sem que o produtor mude uma linha — desacoplamento no sentido mais literal. E EDA é um espectro, não uma religião — um único webhook já é orientado a eventos; um parque de streaming também; a maioria dos sistemas saudáveis é híbrida.
Principais pontos
| Pergunta | Resposta |
|---|---|
| Um evento é | Um fato imutável, no passado — consumidores podem reagir, nunca recusar |
| Evento ≠ comando | Fatos transmitidos a quem se importar vs. imperativos dirigidos a um handler |
| A regra de decisão | Trabalho do produtor termina antes das reações → eventos; quem chama precisa da resposta → request-response |
| Os três sabores | Notification · event-carried state · event sourcing (uma escolha de armazenamento) |
| A conta permanente | Consumidores idempotentes, IDs de correlação, UX de consistência eventual |
Um fato, muitas reações
// JavaScript — Cloud Code (cloud/main.js)
// Everyday EDA: the write IS the event; consumers react independently
Parse.Cloud.afterSave('Order', async (req) => {
const order = req.object;
const becamePlaced = order.get('status') === 'placed' &&
req.original?.get('status') !== 'placed';
if (!becamePlaced) return;
// Consumers of one fact — none knows about the others:
await Parse.Cloud.httpRequest({ // fulfillment system (webhook)
method: 'POST', url: process.env.FULFILL_WEBHOOK,
body: { event: 'order.placed', id: order.id },
});
await enqueueConfirmationEmail(order); // async job queue
// Live Query pushes the change to every open dashboard automatically
}); // Flutter / Dart — Back4app Flutter SDK
// The client as event consumer: subscribe to facts, react on arrival
final orders = QueryBuilder<ParseObject>(ParseObject('Order'))
..whereEqualTo('status', 'placed');
final sub = await LiveQuery().client.subscribe(orders);
sub.on(LiveQueryEvent.create, (order) => showNewOrder(order));
// Nobody called this screen — it REACTED to an event, like every
// other consumer of "order placed": fulfillment, email, analytics. // iOS / Swift — Back4app Swift SDK
// The client as event consumer: subscribe to facts, react on arrival
let orders = Order.query("status" == "placed")
let sub = try await orders.subscribe()
sub.handleEvent { _, event in
if case .created(let order) = event { showNewOrder(order) }
}
// Nobody called this screen — it REACTED to an event, like every
// other consumer of "order placed": fulfillment, email, analytics. // Android / Kotlin — Back4app Android SDK
// The client as event consumer: subscribe to facts, react on arrival
val orders = ParseQuery.getQuery<ParseObject>("Order")
orders.whereEqualTo("status", "placed")
val sub = ParseLiveQueryClient.Factory.getClient().subscribe(orders)
sub.handleEvent(SubscriptionHandling.Event.CREATE) { _, order ->
showNewOrder(order)
}
// Nobody called this screen — it REACTED to an event, like every
// other consumer of "order placed": fulfillment, email, analytics. Um fato — um pedido foi feito — e quatro consumidores, nenhum ciente dos demais: um sistema de fulfillment notificado por webhook, um job de e-mail enfileirado, o analytics contando, dashboards atualizando por uma assinatura ao vivo. Adicionar um quinto consumidor não muda nada upstream. Esse é o argumento inteiro, em um único save.
O vocabulário, desembaraçado
O caos terminológico da busca, resolvido em uma tabela — três tipos de mensagem por intenção, não por transporte:
| Comando | Evento | Consulta | |
|---|---|---|---|
| Gramática | Imperativo: ShipOrder | Passado: OrderShipped | Interrogativa |
| Audiência | Um handler | Quem tiver assinado | Um respondente |
| Pode ser recusado | Sim | Não — já aconteceu | n/a |
| Acoplamento | O remetente conhece o handler | O produtor não conhece ninguém | Quem chama espera |
O alerta de Fowler merece a citação: cuidado com o comando passivo-agressivo — um “evento” que o produtor emite exigindo secretamente que um consumidor específico aja. Se o fluxo quebra quando esse consumidor não responde, era um comando desde o início, e chamá-lo de evento só escondeu o acoplamento. (As palavras de infraestrutura — broker, roteador, barramento, canal, tópico — são quase sinônimos para a caixa do meio; o verbete de pub/sub cobre essa maquinaria, da qual a EDA é a arquitetura generalizante.)
Orientado a eventos vs. request-response
| Request-response | Orientado a eventos | |
|---|---|---|
| Acoplamento no tempo | Quem chama espera — o outro lado precisa estar no ar | Fire-and-forget — consumidores se atualizam depois |
| A resposta | Retorna a quem chamou | Não há resposta, só reações |
| Modo de falha | Erro imediato e visível | Resiliente — e eventualmente consistente |
| Debug | Uma call stack | IDs de correlação através dos saltos |
| Certo para | Leituras, logins, pagamentos — tudo que quem chama precisa | Fatos com consumidores independentes |
A regra de uma frase: se quem chama precisa do resultado para continuar, request-response; se o trabalho do produtor está completo antes de qualquer reação começar, eventos. Fazer o pedido precisa de uma resposta síncrona (“deu certo?”); tudo que vem depois — e-mail, fulfillment, analytics — são reações a um fato, e é por isso que sistemas reais são híbridos por design, não por concessão.
Os três sabores de Fowler — e onde o event sourcing realmente se encaixa
| Padrão | O evento carrega | Consumidores | Troca |
|---|---|---|---|
| Event notification | Um ID e pouco mais | Chamam de volta pelos detalhes | Simples; o tráfego de callbacks reacopla |
| Event-carried state transfer | Todo o estado relevante | Mantêm cópias locais | Sem callbacks; duplicação e desatualização |
| Event sourcing | É o sistema de registro | Reconstroem o estado por replay | Auditoria perfeita; um compromisso pesado de armazenamento |
A desambiguação que economiza reuniões: event sourcing é como um sistema armazena dados; EDA é como sistemas conversam. Eles se compõem, mas nenhum exige o outro. Mais uma linha que os fornecedores borram: a entrega pub/sub esquece — assinantes atrasados perdem o que disparou antes de chegarem — enquanto o streaming (o log ordenado ao estilo Kafka) lembra, permitindo que consumidores reproduzam de qualquer ponto; escolha conforme a história fizer ou não parte do produto.
Coreografia vs. orquestração
| Coreografia (nativa da EDA) | Orquestração | |
|---|---|---|
| Coordenação | Serviços reagem aos eventos uns dos outros | Um coordenador comanda cada passo |
| O fluxo mora | Em lugar nenhum explicitamente — emergente | Em um lugar visível e depurável |
| Acoplamento | Mínimo | O orquestrador conhece todo mundo |
| Tratamento de falhas | Eventos de compensação, distribuído | Retry e lógica de erro centralizados |
A regra espelhada, compartilhada com o verbete de orquestração: orquestre quando alguém precisa ser dono do resultado de um fluxo; coreografe quando os produtores genuinamente não se importam com o que vem depois. Transações de negócio longas entre serviços tomam a forma de saga em ambos os casos — transações locais encadeadas por eventos ou por um orquestrador, desfeitas por compensações em vez de rollback.
EDA no dia a dia — sem broker
A seção que os centros de arquitetura nunca escrevem: a maioria dos desenvolvedores de apps já roda padrões orientados a eventos sem nenhum broker de mensagens à vista. Um trigger beforeSave/afterSave é um consumidor de eventos de mudança de dados. Um webhook é um evento cruzando fronteiras entre empresas por HTTP puro. Uma live query é pub/sub com clientes como assinantes. Um job em segundo plano enfileirado por um trigger é um evento gerando trabalho assíncrono. As abas de código acima são exatamente essa pilha — a escrita é o evento, o trigger é o roteador, e os consumidores são um webhook, uma fila e uma assinatura. Brokers dedicados (Kafka, RabbitMQ, NATS — com o CloudEvents como envelope padrão) justificam seu peso operacional quando o volume de eventos, o replay e os contratos entre times exigem; a arquitetura começa bem antes, e “preciso de um broker?” costuma se responder sozinho: ainda não.
Casos de uso comuns
- Fluxos de pedido e pagamento — um fato, muitos departamentos: o pipeline canônico de e-commerce.
- Integração entre sistemas — serviços de times diferentes reagindo sem chamadas diretas nem deploys compartilhados.
- Recursos em tempo real — dashboards, feeds e presença como consumo de eventos voltado ao cliente.
- Cargas com picos — filas amortecendo rajadas para os consumidores drenarem no próprio ritmo.
- Auditoria e replay — logs de streaming em que “o que aconteceu, em ordem” é o próprio produto.
Você deveria adotar a arquitetura orientada a eventos? Matriz de decisão
| Situação | Tendência |
|---|---|
| Um fato, vários consumidores independentes | Eventos — o jogo em casa |
| Quem chama precisa da resposta para prosseguir | Request-response, sem pedir desculpas |
| Fluxo com dono e SLA | Orquestração — comande os passos |
| Reações dentro de um app (e-mail no cadastro) | Triggers + jobs — EDA do dia a dia |
| Notificações entre empresas | Webhooks |
| Streams de alto volume, com replay e multi-time | Um broker de verdade — agora ele se paga |
Limitações e trade-offs
- O debug perde a call stack. Uma requisição que se espalha em cinco saltos assíncronos só é rastreável por IDs de correlação carregados desde o primeiro evento — retrofit é sofrido; instale desde o primeiro dia.
- A consistência eventual chega à UI. O usuário atualiza o perfil e a tela seguinte mostra o nome antigo; desenhe a UX para isso ou mantenha esse caminho síncrono.
- Duplicatas são contratuais. Entrega at-least-once torna consumidores idempotentes obrigatórios — o mesmo contrato que as filas impõem, porque é a mesma maquinaria.
- Schemas evoluem sob os pés dos consumidores. Eventos são contratos com assinantes desconhecidos: só mudanças aditivas, tópicos versionados para quebras e leitores tolerantes em toda parte.
- Fluxos emergentes se escondem. Coreografia pura significa que ninguém consegue apontar o processo de negócio; quando auditores ou o plantão precisarem, adicione o orquestrador.
Padrões orientados a eventos no Back4app
O Back4app é uma plataforma open-source de Backend as a Service (BaaS) que combina banco de dados gerenciado, APIs REST e GraphQL geradas automaticamente, autenticação, armazenamento de arquivos e funções serverless com Cloud Code. A seção de EDA do dia a dia é a lista de recursos dele lida como arquitetura: cada save é um evento, triggers afterSave são consumidores server-side com o ecossistema JavaScript completo, as Live Queries espalham os fatos aos clientes por WebSockets gerenciados, webhooks os levam a sistemas externos e jobs em segundo plano absorvem as reações lentas — as abas de código mostram o ciclo inteiro a partir de um save de pedido. As disciplinas que este artigo cobra continuam suas (after-hooks idempotentes, pensamento no passado, síncrono onde quem chama precisa de resposta), enquanto a infraestrutura em forma de broker — fan-out, entrega, frotas de conexões — chega como comportamento de plataforma, e graduar-se depois para um broker de streaming dedicado é uma adição a esta arquitetura, não uma reescrita dela.
Perguntas frequentes
O que é arquitetura orientada a eventos em termos simples?
Componentes anunciam que algo aconteceu — um evento — em vez de chamarem uns aos outros, e qualquer componente interessado reage no seu próprio ritmo. Transmissão de rádio em vez de telefonema: o trabalho de quem anuncia termina no anúncio, e os ouvintes entram e saem sem que o anunciante mude uma linha.
Qual a diferença entre evento e comando?
Intenção. Um comando é um imperativo dirigido a um destinatário, que pode recusá-lo — ShipOrder. Um evento é um fato no passado transmitido a quem se importar — OrderShipped. A armadilha é o "comando passivo-agressivo": um evento que secretamente espera que um consumidor específico aja, ou seja, um comando fantasiado de evento.
Qual a diferença entre orientado a eventos e request-response?
Acoplamento no tempo. Request-response é síncrono — quem chama espera, precisa da resposta e falha se o outro lado estiver fora do ar. Eventos são fire-and-forget — resilientes e desacoplados, mas eventualmente consistentes. A regra: se quem chama precisa do resultado para continuar, request-response; se o trabalho do produtor termina antes de as reações começarem, eventos.
Qual a diferença entre EDA e pub/sub?
Nível. Pub/sub é um padrão de mensageria — um mecanismo de entrega: publicadores para tópicos, tópicos para assinantes. EDA é o estilo arquitetural que normalmente roda sobre ele, junto com filas e streams. Pub/sub é o encanamento; orientado a eventos é a filosofia de design que esse encanamento serve.
O que são event notification, event-carried state transfer e event sourcing?
Os três padrões distintos de Fowler escondidos sob um mesmo nome. Notification: eventos magros; consumidores chamam de volta pelos detalhes. State transfer: eventos carregam os dados completos, e consumidores mantêm cópias locais. Sourcing: uma escolha de persistência — armazenar cada mudança como evento e reconstruir o estado por replay. O terceiro é como um sistema guarda dados, não como sistemas conversam.
Qual a diferença entre coreografia e orquestração?
Coreografia: serviços reagem aos eventos uns dos outros, sem cérebro central — desacoplamento máximo, mas o fluxo não existe explicitamente em lugar nenhum. Orquestração: um coordenador comanda cada passo — visível e depurável, ao custo de um ponto central de acoplamento. Regra: orquestre quando alguém precisa ser dono do resultado; coreografe quando os produtores genuinamente não se importam com o que vem depois.
Como lidar com eventos duplicados e fora de ordem?
Por design, não por esperança: brokers entregam at-least-once, então consumidores precisam ser idempotentes — deduplique por ID de evento ou escreva handlers cuja repetição seja inofensiva ("definir status como enviado", nunca "alternar"). A ordem só é garantida por partição ou chave; arquiteturas que exigem ordem global brigam com o próprio transporte.
Quando usar arquitetura orientada a eventos?
Quando um fato tem vários consumidores independentes, quando a carga tem picos e um buffer ajuda, quando sistemas precisam se integrar sem se conhecer, ou quando uma trilha de auditoria das mudanças tem valor em si. Quando não: CRUD simples, leituras, operações que exigem consistência forte imediata e times sem apetite para sistemas distribuídos.