Fluxos orientados a eventos vs APIs request-response para tarefas longas
Compare fluxos orientados a eventos vs APIs request-response para processos longos, focando em aprovações, timers, tentativas e trilhas de auditoria em apps de negócio.

Por que processos de longa duração são complicados em apps de negócio
Um processo é “de longa duração” quando não pode ser concluído em um passo rápido. Pode levar minutos, horas ou dias porque depende de pessoas, tempo ou sistemas externos. Tudo que tenha aprovações, repasses e espera entra nessa categoria.
É aí que o pensamento simples de API request-response começa a falhar. Uma chamada de API foi feita para uma troca curta: envie uma requisição, receba uma resposta, siga em frente. Tarefas longas são mais como uma história com capítulos. Você precisa pausar, lembrar exatamente onde está e continuar depois sem adivinhar.
Você vê isso em apps de negócios do dia a dia: aprovações de compras que precisam do gerente e da área financeira, integração de funcionários que espera checagens de documentos, reembolsos dependentes de um provedor de pagamento, ou pedidos de acesso que devem ser revisados e depois aplicados.
Quando equipes tratam um processo longo como uma única chamada de API, alguns problemas previsíveis aparecem:
- O app perde estado após um restart ou deploy e não consegue retomar de forma confiável.
- Tentativas criam duplicatas: um segundo pagamento, um segundo e-mail, uma aprovação duplicada.
- Responsabilidade fica confusa: ninguém sabe se o solicitante, um gerente ou um job do sistema deve agir a seguir.
- Suporte não tem visibilidade e não consegue responder “onde está travado?” sem cavar nos logs.
- Lógica de espera (timers, lembretes, deadlines) vira scripts frágeis em background.
Um cenário concreto: um funcionário solicita acesso a um software. O gerente aprova rápido, mas o time de TI precisa de dois dias para provisionar. Se o app não conseguir manter o estado do processo, enviar lembretes e retomar com segurança, você terá acompanhamentos manuais, usuários confusos e trabalho extra.
Por isso a escolha entre fluxos orientados a eventos vs APIs request-response importa para processos empresariais de longa duração.
Dois modelos mentais: chamadas síncronas vs eventos ao longo do tempo
A comparação mais simples se resume a uma pergunta: o trabalho termina enquanto o usuário espera, ou continua depois que ele sai?
Uma API request-response é uma troca única: uma chamada entra, uma resposta sai. Cabe a trabalhos que terminam rápido e previsivelmente, como criar um registro, calcular um orçamento ou checar estoque. O servidor faz o trabalho, retorna sucesso ou falha, e a interação acaba.
Um fluxo orientado a eventos é uma série de reações ao longo do tempo. Algo acontece (um pedido é criado, um gerente aprova, um timer expira) e o workflow avança para a próxima etapa. Esse modelo serve a trabalhos que incluem repasses, espera, tentativas e lembretes.
A diferença prática é o estado.
Com request-response, o estado frequentemente vive na requisição atual mais a memória do servidor até a resposta ser enviada. Com fluxos orientados a eventos, o estado precisa ser armazenado (por exemplo em PostgreSQL) para que o processo possa retomar depois.
O tratamento de falhas também muda. Request-response geralmente lida com falhas retornando um erro e pedindo ao cliente para tentar novamente. Workflows registram a falha e podem tentar de novo com segurança quando as condições melhoram. Eles também podem registrar cada passo como um evento, o que facilita reconstruir o histórico.
Um exemplo simples: “Enviar relatório de despesas” pode ser síncrono. “Obter aprovação, esperar 3 dias, lembrar o gerente, então pagar” não é.
Aprovações: como cada abordagem lida com decisões humanas
Aprovações são onde o trabalho de longa duração vira concreto. Um passo de sistema termina em milissegundos, mas uma pessoa pode responder em dois minutos ou dois dias. A escolha de projeto é modelar essa espera como um processo pausado, ou como uma nova mensagem que chega depois.
Com APIs request-response, aprovações muitas vezes assumem formas desconfortáveis:
- Bloqueio (não prático)
- Polling (o cliente pergunta “aprovou?” repetidamente)
- Callbacks/webhooks (o servidor te chama de volta mais tarde)
Tudo isso pode funcionar, mas adiciona encanamento só para ligar “tempo humano” ao “tempo de API”.
Com eventos, a aprovação lê como uma história. O app registra algo como “ExpenseSubmitted”, e mais tarde recebe “ExpenseApproved” ou “ExpenseRejected”. O motor de workflow (ou sua própria máquina de estados) move o registro adiante somente quando o próximo evento chega. Isso bate com a forma como a maioria das pessoas já pensa sobre passos de negócio: submeter, revisar, decidir.
A complexidade aparece rápido com múltiplos aprovadores e regras de escalonamento. Você pode exigir gerente e financeiro, mas permitir que um gerente sênior sobreponha. Se não modelar essas regras claramente, o processo fica difícil de raciocinar e ainda mais difícil de auditar.
Um modelo simples de aprovação que escala
Um padrão prático é manter um único registro de “solicitação” e armazenar decisões separadamente. Assim você consegue suportar vários aprovadores sem reescrever a lógica núcleo.
Capture alguns dados como registros de primeira classe:
- A própria solicitação de aprovação: o que está sendo aprovado e seu status atual
- Decisões individuais: quem decidiu, aprovar/rejeitar, timestamp, motivo
- Aprovadores requeridos: papel ou pessoa, e regras de ordenação
- Regras de resultado: “qualquer um”, “maioria”, “todos obrigatórios”, “override permitido”
Qualquer que seja a implementação, sempre armazene quem aprovou o quê, quando e por quê como dados, não como uma linha de log.
Timers e espera: lembretes, prazos e escalonamentos
Esperar é onde tarefas longas começam a ficar bagunçadas. Pessoas almoçam, agendas lotam e “vamos responder” vira “quem é o dono disso agora?” Essa é uma das diferenças mais claras entre fluxos orientados a eventos vs APIs request-response.
Com request-response, tempo é desconfortável. Chamadas HTTP têm timeout, então você não consegue manter uma requisição aberta por dois dias. Equipes normalmente acabam com padrões como polling, um job agendado que varre o banco de dados ou scripts manuais quando algo está atrasado. Isso pode funcionar, mas a lógica de espera fica fora do processo. Casos de canto são fáceis de perder, como o que acontece quando o job roda duas vezes, ou quando o registro mudou pouco antes do lembrete ser enviado.
Workflows tratam o tempo como um passo normal. Você pode dizer: espere 24 horas, envie um lembrete, então espere até 48 horas no total e escale para outro aprovador. O sistema mantém o estado, então prazos não ficam escondidos em um projeto separado de “cron + queries”.
Uma regra simples de aprovação poderia ser assim:
Depois que um relatório de despesas é submetido, espere 1 dia. Se o status ainda for “Pending”, avise o gerente. Depois de 2 dias, se ainda estiver pendente, realoque para o líder do gerente e registre a escalada.
O detalhe chave é o que fazer quando o timer dispara mas o mundo mudou. Um bom workflow sempre re-checa o estado atual antes de agir:
- Carregar o status mais recente
- Confirmar que ainda está pendente
- Confirmar que o assignee ainda é válido (mudanças de time acontecem)
- Registrar o que foi decidido e por quê
Tentativas e recuperação de falhas sem ações duplicadas
Tentativas (retries) são o que você faz quando algo falhou por razões fora do seu controle: gateway de pagamento com timeout, provedor de e-mail com erro temporário, ou seu app salva o passo A mas cai antes do passo B. O perigo é simples: você tenta de novo e faz a ação duas vezes.
Com request-response, o padrão comum é que o cliente chama um endpoint, espera e, se não obtiver sucesso claro, tenta novamente. Para tornar isso seguro, o servidor precisa tratar chamadas repetidas como a mesma intenção.
Uma correção prática é uma chave de idempotência: o cliente envia um token único como pay:invoice-583:attempt-1. O servidor armazena o resultado para essa chave e retorna o mesmo resultado para repetições. Isso evita cobranças em duplicidade, tickets duplicados ou aprovações repetidas.
Fluxos orientados a eventos têm outro tipo de risco de duplicação. Eventos frequentemente são entregues pelo menos uma vez (at-least-once), o que significa que duplicatas podem aparecer mesmo quando tudo está funcionando. Consumidores precisam deduplicar: registre o ID do evento (ou uma chave de negócio como invoice_id + step) e ignore repetições. Essa é uma diferença central nos padrões de orquestração: request-response foca em replays seguros de chamadas, enquanto eventos focam em replays seguros de mensagens.
Algumas regras de retry funcionam bem em qualquer modelo:
- Use backoff (por exemplo 10s, 30s, 2m).
- Defina um limite máximo de tentativas.
- Separe erros temporários (retry) de erros permanentes (fail fast).
- Encaminhe falhas repetidas para um estado “precisa de atenção”.
- Faça log de cada tentativa para explicar o que aconteceu depois.
Tentativas devem ser explícitas no processo, não comportamento escondido. Assim você torna falhas visíveis e corrigíveis.
Trilhas de auditoria: tornar o processo explicável
Uma trilha de auditoria é seu arquivo do “por quê”. Quando alguém pergunta “Por que essa despesa foi rejeitada?” você deve ser capaz de responder sem adivinhar, mesmo meses depois. Isso importa tanto em fluxos orientados a eventos quanto em APIs request-response, mas o trabalho fica diferente.
Para qualquer processo de longa duração, registre os fatos que permitem reproduzir a história:
- Ator: quem fez (usuário, serviço ou timer do sistema)
- Hora: quando aconteceu (com fuso horário)
- Input: o que era conhecido então (valor, fornecedor, thresholds da política, aprovações)
- Output: qual decisão ou ação aconteceu (aprovado, rejeitado, pago, tentado novamente)
- Versão da regra: qual versão da política/ lógica foi usada
Fluxos orientados a eventos podem facilitar auditoria porque cada passo naturalmente produz um evento como “ManagerApproved” ou “PaymentFailed”. Se você armazenar esses eventos com payload e ator, obtém uma linha do tempo limpa. O importante é manter eventos descritivos e guardá-los onde você possa consultar por caso.
APIs request-response ainda podem ser auditáveis, mas a história costuma ficar espalhada por serviços. Um endpoint registra “aprovado”, outro registra “pagamento solicitado” e um terceiro registra “retry succeeded”. Se cada um usar formatos ou campos diferentes, auditorias viram trabalho de detetive.
Uma correção simples é um “case ID” compartilhado (também chamado correlation ID). É um identificador que você anexa a toda requisição, evento e registro do banco para a instância do processo, como “EXP-2026-00173”. Assim você consegue traçar toda a jornada.
Escolhendo a abordagem certa: forças e trocas
A melhor escolha depende de você precisar de uma resposta imediata ou de o processo continuar por horas ou dias.
Request-response funciona bem quando o trabalho é curto e as regras são simples. Um usuário submete um formulário, o servidor valida, salva dados e retorna sucesso ou erro. Também cabe ações claras e de passo único como create, update ou check permissions.
Começa a doer quando uma “única requisição” vira vários passos: esperar aprovação, chamar múltiplos sistemas externos, lidar com timeouts ou ramificações baseadas no que acontece a seguir. Você ou mantém uma conexão aberta (frágil), ou empurra espera e retries para jobs background difíceis de raciocinar.
Fluxos orientados a eventos brilham quando o processo é uma história ao longo do tempo. Cada passo reage a um novo evento (aprovado, rejeitado, timer disparado, pagamento falhou) e decide o que fazer depois. Isso torna mais fácil pausar, retomar, retry e manter uma trilha clara do porquê o sistema agiu assim.
Há trocas reais:
- Simplicidade vs durabilidade: request-response é mais simples para começar, eventos são mais seguros para longas esperas.
- Estilo de debug: request-response segue uma linha reta, workflows exigem traçar passos entre etapas.
- Ferramentas e hábitos: eventos precisam de bom logging, correlation IDs e modelos de estado claros.
- Gestão de mudanças: workflows evoluem e ramificam; designs orientados a eventos tendem a lidar melhor com novos caminhos quando bem modelados.
Um exemplo prático: um relatório de despesas que precisa de aprovação do gerente, depois revisão financeira e então pagamento. Se o pagamento falha, você quer retries sem pagar duas vezes. Isso é naturalmente orientado a eventos. Se for apenas “submeter despesa” com checagens rápidas, request-response geralmente basta.
Passo a passo: desenhando um processo de longa duração que sobrevive a atrasos
Processos longos falham de maneiras chatas: uma aba do navegador fecha, um servidor reinicia, uma aprovação fica três dias pendente, ou um provedor de pagamento dá timeout. Projete pensando nesses atrasos desde o início, independentemente do modelo escolhido.
Comece definindo um pequeno conjunto de estados que você possa armazenar e retomar. Se você não consegue apontar o estado atual no banco de dados, não tem realmente um workflow retomável.
Uma sequência de design simples
- Defina limites: gatilho de início, condição de término e alguns estados chave (Pending approval, Approved, Rejected, Expired, Completed).
- Nomeie eventos e decisões: escreva o que pode acontecer ao longo do tempo (Submitted, Approved, Rejected, TimerFired, RetryScheduled). Mantenha nomes de eventos no passado.
- Escolha pontos de espera: identifique onde o processo pausa por um humano, um sistema externo ou um deadline.
- Adicione regras de timer e retry por etapa: decida o que acontece quando o tempo passa ou uma chamada falha (backoff, tentativas máximas, escalar, desistir).
- Defina como o processo retoma: em cada evento ou callback, carregue o estado salvo, verifique se ainda é válido e então avance ao próximo estado.
Para sobreviver a reinícios, persista o mínimo de dados necessários para continuar com segurança. Guarde o suficiente para re-executar sem adivinhar:
- ID da instância do processo e estado atual
- Quem pode agir a seguir (assignee/role) e o que já decidiram
- Deadlines (due_at, remind_at) e nível de escalonamento
- Metadados de retry (contagem de tentativas, último erro, next_retry_at)
- Chave de idempotência ou flags de "já feito" para efeitos colaterais (envio de mensagem, cobrança)
Se você consegue reconstruir “onde estamos” e “o que é permitido a seguir” a partir dos dados guardados, atrasos deixam de ser assustadores.
Erros comuns e como evitá-los
Processos longos frequentemente quebram só quando usuários reais aparecem. Uma aprovação leva dois dias, um retry dispara na hora errada e você acaba com pagamento em duplicidade ou trilha de auditoria incompleta.
Erros comuns:
- Manter uma requisição HTTP aberta enquanto espera por uma aprovação humana. Ela expira, consome recursos do servidor e dá a sensação errada de que “algo está acontecendo”.
- Retentar chamadas sem idempotência. Uma falha de rede vira faturas duplicadas, e-mails repetidos ou transições “Aprovado” duplicadas.
- Não persistir estado do processo. Se o estado vive em memória, um restart o apaga. Se vive só em logs, você não consegue continuar de forma confiável.
- Construir uma trilha de auditoria confusa. Eventos têm relógios e formatos diferentes, então a linha do tempo não pode ser confiável durante um incidente ou auditoria.
- Misturar async e sync sem uma única fonte de verdade. Um sistema diz “Pago”, outro diz “Pendente” e ninguém sabe qual está certo.
Um exemplo simples: um relatório de despesas é aprovado no chat, um webhook chega atrasado e a API de pagamento é tentada novamente. Sem estado persistido e idempotência, o retry pode pagar duas vezes, e seus registros não explicarão claramente o porquê.
A maioria das correções é ser explícito:
- Persista transições de estado (Requested, Approved, Rejected, Paid) em um banco, com quem/qué mudou.
- Use chaves de idempotência para cada efeito externo (pagamentos, e-mails, tickets) e armazene o resultado.
- Separe “aceitar a solicitação” de “finalizar o trabalho”: responda rápido, depois complete o workflow em background.
- Padronize timestamps (UTC), adicione correlation IDs e registre tanto a requisição quanto o resultado.
Checklist rápido antes de construir
Trabalho de longa duração é menos sobre uma chamada perfeita e mais sobre permanecer correto após atrasos, pessoas e falhas.
Escreva o que “seguro para continuar” significa para seu processo. Se o app reiniciar no meio, você deve conseguir retomar do último passo conhecido sem adivinhar.
Um checklist prático:
- Defina como o processo retoma após crash ou deploy. Que estado é salvo e o que roda em seguida?
- Dê a cada instância uma chave única (como ExpenseRequest-10482) e um modelo de status claro (Submitted, Waiting for Manager, Approved, Paid, Failed).
- Trate aprovações como registros, não só resultados: quem aprovou ou rejeitou, quando e o motivo/comentário.
- Mapeie regras de espera: lembretes, prazos, escalonamentos, expirações. Nomeie um dono para cada timer (manager, finance, system).
- Planeje tratamento de falhas: retries devem ser limitados e seguros, e deve haver uma parada “needs review” onde uma pessoa possa corrigir dados ou aprovar uma nova tentativa.
Um teste de sanidade: imagine que um provedor de pagamento deu timeout depois que você já cobrou o cartão. Seu desenho deve impedir cobrar duas vezes, ao mesmo tempo em que permite que o processo termine.
Exemplo: aprovação de despesa com prazo e retry de pagamento
Cenário: um funcionário submete um recibo de táxi de $120 para reembolso. Precisa de aprovação do gerente em 48 horas. Se aprovado, o sistema paga ao funcionário. Se o pagamento falhar, ele tenta de novo com segurança e deixa um registro claro.
Roteiro request-response
Com APIs request-response, o app costuma se comportar como uma conversa que precisa ficar checando. O funcionário toca em Submit. O servidor cria um registro de reembolso com status “Pending approval” e retorna um ID. O gerente recebe uma notificação, mas o app do funcionário geralmente precisa fazer polling para ver se algo mudou, por exemplo: “GET reimbursement status by ID.”
Para impor o prazo de 48 horas, você ou roda um job agendado que varre solicitações vencidas, ou armazena um timestamp de deadline e verifica durante os polls. Se o job atrasar, os usuários veem status desatualizado.
Quando o gerente aprova, o servidor muda para “Approved” e chama o provedor de pagamento. Se Stripe retornar um erro temporário, o servidor precisa decidir tentar agora, agendar uma nova tentativa ou falhar. Sem chaves de idempotência cuidadosas, um retry pode gerar um pagamento em duplicidade.
Roteiro orientado a eventos
No modelo orientado a eventos, cada mudança é um fato registrado.
O funcionário submete, gerando um evento “ExpenseSubmitted”. Um workflow começa e espera por “ManagerApproved” ou por um evento timer “DeadlineReached” em 48 horas. Se o timer disparar primeiro, o workflow registra um resultado “AutoRejected” e o motivo.
Na aprovação, o workflow registra “PayoutRequested” e tenta o pagamento. Se Stripe der timeout, ele registra “PayoutFailed” com um código de erro, agenda um retry (por exemplo em 15 minutos) e só registra “PayoutSucceeded” uma vez, usando uma chave de idempotência.
O que o usuário vê permanece simples:
- Pending approval (48 hours left)
- Approved, paying out
- Payment retry scheduled
- Paid
A trilha de auditoria lê como uma timeline: submitted, approved, deadline checked, payout attempted, failed, retried, paid.
Próximos passos: transformar o modelo em um app funcionando
Escolha um processo real e construa de ponta a ponta antes de generalizar. Aprovação de despesas, onboarding e tratamento de reembolsos são bons pontos de partida porque incluem passos humanos, espera e caminhos de erro. Mantenha o objetivo pequeno: um happy path e as duas exceções mais comuns.
Escreva o processo como estados e eventos, não como telas. Por exemplo: “Submitted” -> “ManagerApproved” -> “PaymentRequested” -> “Paid”, com ramificações como “ApprovalRejected” ou “PaymentFailed”. Quando você vê claramente os pontos de espera e efeitos colaterais, a escolha entre fluxos orientados a eventos vs APIs request-response fica prática.
Decida onde o estado do processo vive. Um banco de dados pode ser suficiente se o fluxo for simples e você conseguir garantir atualizações em um só lugar. Um motor de workflow ajuda quando você precisa de timers, retries e branching, porque ele controla o que deve acontecer a seguir.
Adicione campos de auditoria desde o primeiro dia. Armazene quem fez o quê, quando aconteceu e por quê (comentário ou código de motivo). Quando alguém perguntar “Por que esse pagamento foi re-tentado?” você quer uma resposta clara sem ter que vasculhar logs.
Se você estiver construindo esse tipo de workflow em uma plataforma no-code, AppMaster (appmaster.io) é uma opção onde você pode modelar dados em PostgreSQL e criar a lógica do processo visualmente, o que pode facilitar manter aprovações e trilhas de auditoria consistentes entre web e apps móveis.
FAQ
Use request-response quando o trabalho terminar rápida e previsivelmente enquanto o usuário espera, como criar um registro ou validar um formulário. Use um fluxo orientado a eventos quando o processo durar minutos ou dias, incluir aprovações humanas ou precisar de timers, tentativas e retomada segura após reinícios.
Tarefas longas não cabem em uma única requisição HTTP porque conexões expiram, servidores reiniciam e o trabalho depende de pessoas ou sistemas externos. Se você tratar tudo como uma única chamada, normalmente perde estado, cria duplicações ao tentar novamente e acaba com scripts de background espalhados para lidar com esperas.
Um bom padrão é persistir um estado claro do processo no banco de dados e avançar esse estado apenas por transições explícitas. Armazene o ID da instância do processo, o status atual, quem pode agir a seguir e os timestamps-chave para que você possa retomar com segurança após deploys, falhas ou atrasos.
Modele aprovações como uma etapa pausada que retoma quando chega uma decisão, em vez de bloquear ou ficar em polling constante. Registre cada decisão como dados (quem decidiu, quando, aprovar/rejeitar e motivo) para que o fluxo possa avançar de forma previsível e você possa auditar depois.
O polling pode funcionar em casos simples, mas adiciona ruído e atrasos porque o cliente fica perguntando “já terminou?”. Um padrão melhor é enviar uma notificação quando houver mudança e deixar o cliente atualizar sob demanda, enquanto o servidor é a fonte de verdade do estado.
Trate o tempo como parte do processo: armazene deadlines e horários de lembrete, e ao disparar um timer sempre verifique o estado atual antes de agir. Assim você evita enviar lembretes depois que algo já foi aprovado e mantém as escaladas consistentes mesmo se jobs rodarem atrasados ou duas vezes.
Comece com chaves de idempotência para qualquer efeito colateral (cobrar um cartão, enviar um e-mail) e armazene o resultado para essa chave. Assim, repetir a mesma intenção retorna o mesmo resultado em vez de executar a ação novamente.
Pressuponha que mensagens possam ser entregues mais de uma vez e faça consumidores que deduplicam. Uma abordagem prática é armazenar o ID do evento (ou uma chave de negócio para a etapa) e ignorar repetidos para que um replay não dispare a mesma ação duas vezes.
Capture uma linha do tempo de fatos: ator, timestamp, o input disponível então, o resultado e a versão da regra/política usada. Também use um único case ID ou correlation ID para tudo relacionado ao processo, assim o suporte pode responder “onde está preso?” sem vasculhar logs desconexos.
Mantenha um registro principal do pedido como o “caso”, armazene decisões separadamente e conduza mudanças de estado por transições persistidas que possam ser reproduzidas. Em ferramentas no-code como AppMaster, você pode modelar dados em PostgreSQL e implementar a lógica de etapas visualmente, o que ajuda a manter aprovações, tentativas e campos de auditoria consistentes.


