Um processo de negócio pode concluir parte do trabalho e falhar antes de terminar. Por exemplo, uma aplicação PHP cria uma conta no próprio banco de dados, mas não consegue provisioná-la em um serviço externo. Nesse momento, repetir o fluxo sem mais pode duplicar efeitos; considerá-lo malsucedido pode deixar os sistemas incoerentes. As compensações em processos distribuídos com PHP permitem definir como responder a esses resultados parciais, sem presumir que vários sistemas participem de uma única transação atômica.
A decisão importante não é apenas como desfazer o que aconteceu. Alguns efeitos não podem ser revertidos exatamente, e uma compensação também pode falhar. O projeto deve tornar visível o estado real da operação, permitir sua recuperação e indicar quando é necessária intervenção humana.
Transações locais, novas tentativas e compensações resolvem problemas diferentes

Uma transação de banco de dados protege alterações dentro dos limites desse banco de dados. Se o processo salvar várias linhas e uma operação falhar antes do COMMIT, pode executar um ROLLBACK para descartar essas alterações. Mas esse rollback não retira um e-mail enviado, não cancela automaticamente uma solicitação já aceita por uma API e não reverte alterações confirmadas em outro banco de dados.
Uma nova tentativa executa novamente uma ação que pode ter falhado por um problema temporário, como um timeout ou uma indisponibilidade. Ela só é apropriada quando as consequências de repeti-la são conhecidas. A solicitação pode ter chegado ao sistema externo mesmo que a resposta tenha se perdido; sem uma chave de idempotência ou uma consulta de status, a nova tentativa pode criar uma duplicata.
Uma compensação executa uma ação de negócio que contrabalança uma etapa já concluída: por exemplo, desativar um acesso que acabou de ser criado. Não é um rollback técnico nem garante recuperar exatamente o estado anterior. Uma reconciliação, por sua vez, compara o que foi registrado localmente com o estado dos sistemas envolvidos e ajuda a detectar e resolver discrepâncias. Ela pode ser necessária mesmo quando há novas tentativas e compensações.
Modele cada operação como uma sequência de etapas observáveis
Antes de escrever handlers de exceção, descreva o fluxo como etapas independentes. Para cada uma, especifique qual sistema ela modifica, qual resultado conta como sucesso, como a operação é identificada, se é seguro repeti-la e qual alternativa existe caso não possa ser concluída. Defina também quais efeitos devem permanecer coerentes; essa regra de negócio é o invariante que orientará a recuperação.
Persista na aplicação um registro da operação e de suas etapas. Um modelo simples pode incluir um identificador estável da operação, o estado geral, o nome da etapa, o resultado, as tentativas, os registros de data e hora e o último erro relevante. Estados como pendente, em andamento, concluído, falhou, compensando e requer revisão ajudam a distinguir o que se sabe daquilo que ainda não foi confirmado.
Grave a alteração local e o registro de trabalho de forma atômica quando eles precisarem permanecer vinculados. Uma fila de trabalho pode processar a chamada externa posteriormente; o padrão caixa de saída é uma opção para evitar que a alteração no banco de dados seja confirmada enquanto a notificação que deveria iniciar a próxima etapa se perde. Isso não elimina falhas de rede: o consumidor ainda precisa tolerar mensagens repetidas e resultados incertos.
Evite manter uma transação de banco de dados aberta durante uma chamada de rede. Chamadas externas têm latência e podem ficar bloqueadas, enquanto a transação prolongada retém recursos e não torna os dois sistemas atômicos. Confirme o trabalho local seguindo uma sequência explícita e registre o necessário para continuar ou compensar.
Projete compensações explícitas, limitadas e idempotentes
Para cada etapa que produz um efeito, decida se há uma compensação e quando ela deve ser executada. A compensação não precisa ser a inversa literal. Se uma conta já recebeu uma notificação, não é possível apagar o fato de que a mensagem foi lida; pode ser apropriado registrar uma correção ou informar o cancelamento. Se uma ação for irreversível ou tiver consequências legais ou financeiras, talvez a resposta correta seja interromper o fluxo e escalá-lo.
- Idempotência: executar a mesma compensação duas vezes não deve criar efeitos adicionais. Use um identificador estável da operação ou uma chave de idempotência quando o sistema permitir, e registre o resultado.
- Ordem: defina quais etapas serão compensadas e em que sequência. Normalmente, tenta-se reverter na ordem inversa à da execução, mas as dependências e regras de negócio podem exigir outra ordem.
- Limites: estabeleça quais erros permitem novas tentativas, quantas tentativas serão feitas e quando parar. Aplique intervalos entre tentativas e evite novas tentativas indefinidas.
- Incerteza: se o tempo limite de uma API expirar, consulte o status ou use uma chave de idempotência antes de repetir uma criação ou executar uma compensação potencialmente incorreta.
A lógica de compensação deve ser uma operação de negócio identificável, não um bloco genérico que apaga dados ao capturar qualquer exceção. Preserve o histórico necessário para explicar o que aconteceu e verificar se a resposta atende às regras do produto.
Exemplo hipotético: criação de uma conta em dois sistemas
Suponhamos um fluxo que cria uma conta local e solicita a um provedor externo a ativação de um espaço de trabalho. A aplicação atribui um identificador de operação e registra a criação como pendente. Em uma transação local, salva a conta e um trabalho para solicitar o provisionamento. Um worker PHP processa o trabalho e registra o identificador externo e o resultado da solicitação.
Se o provedor confirmar a ativação, o fluxo marca a operação como concluída. Se responder com um erro temporário conhecido, o trabalho pode ser repetido com limites e a mesma chave de idempotência. Se a resposta for incerta, a aplicação primeiro tenta descobrir se o espaço de trabalho existe. Se o provedor confirmar que ele foi criado, mas um requisito local posterior impedir a conclusão da criação da conta, o sistema pode solicitar sua desativação como compensação e salvar esse resultado.
Se a desativação falhar, não se deve declarar que a criação da conta foi revertida. A operação permanece em um estado visível de recuperação pendente ou revisão, com os identificadores local e externo, as tentativas e o erro. A equipe de operações precisa de um procedimento para verificar o provedor, decidir a ação permitida e registrar o desfecho. O exemplo é hipotético: as ações concretas dependem dos recursos da API e das regras de negócio.
Quando a compensação também falha: visibilidade e revisão
Um fluxo distribuído não pode prometer que toda ação será desfeita automaticamente. Projete uma saída explícita para o esgotamento das novas tentativas, uma resposta externa ambígua ou a falha da compensação. Preserve o estado sem ocultar a incoerência e evite apresentar a operação ao usuário como concluída se ainda não estiver.
Ofereça ao suporte ou às operações uma visualização que mostre o estado de cada etapa, o último erro, os identificadores de correlação e as ações permitidas. A revisão humana deve ter controles de acesso e deixar uma trilha de auditoria da decisão. Exponha métricas e alertas para detectar operações paralisadas, compensações com falha e discrepâncias que ultrapassem o tempo operacional aceitável. Uma tarefa periódica de reconciliação pode comparar os registros locais com o sistema externo e abrir casos para resolver diferenças.
Teste falhas parciais e decida se você precisa de compensação

Os testes devem cobrir mais do que o caminho de sucesso. Simule erros antes e depois de cada efeito, respostas atrasadas, timeouts com resultado desconhecido, mensagens duplicadas, processos interrompidos e falhas durante a compensação. Verifique o estado persistido após reiniciar o worker e confirme que as novas tentativas não duplicam contas nem outras operações.
- O identificador da operação e os resultados de cada etapa são preservados?
- Uma mensagem repetida produz o mesmo resultado sem efeitos duplicados?
- O status externo é consultado quando a resposta de uma solicitação é incerta?
- A compensação é executada na ordem prevista e é segura se for repetida?
- Uma falha de compensação mantém a operação visível e a encaminha para o processo de revisão?
- É possível reconciliar posteriormente o estado local com o sistema externo?
- Os testes verificam os invariantes de negócio, não apenas os códigos de resposta?
Se o fluxo afetar um único sistema e uma transação local proteger todas as suas alterações, essa transação e uma recuperação simples podem ser suficientes. Se houver efeitos externos repetíveis com garantias conhecidas, talvez bastem novas tentativas idempotentes e reconciliação. Quando uma etapa confirmada precisar ser contrabalançada para preservar uma regra de negócio, defina uma compensação. A complexidade adicional vale a pena quando o custo de deixar estados parciais sem detectar supera o de manter o registro, os testes e o procedimento operacional.



