No post anterior discuti os quatro padrões principais. Aqui entro no código — não receitas prontas, mas as estruturas que fazem a diferença entre uma integração que aguentou dois anos e uma que virou incidente no terceiro mês.
A estrutura que eu uso para chamadas HTTP
Toda chamada HTTP síncrona em Apex deveria ter três camadas:
// 1. Service — lógica de negócio, sem HTTP
public with sharing class EstoqueService {
public static EstoqueResponse consultarEstoque(Id produtoId) {
Product2 produto = [
SELECT Id, ExternalId__c
FROM Product2
WHERE Id = :produtoId
WITH USER_MODE
];
return EstoqueClient.get(produto.ExternalId__c);
}
}
// 2. Client — HTTP puro, sem lógica de negócio
public without sharing class EstoqueClient {
public static EstoqueResponse get(String externalId) {
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:EstoqueAPI/produtos/' + externalId);
req.setMethod('GET');
req.setTimeout(5000);
HttpResponse res = new Http().send(req);
if (res.getStatusCode() != 200) {
throw new EstoqueException('HTTP ' + res.getStatusCode());
}
return (EstoqueResponse) JSON.deserialize(res.getBody(), EstoqueResponse.class);
}
}
// 3. DTO — estrutura da resposta
public class EstoqueResponse {
public Integer quantidade;
public String unidade;
public Datetime atualizadoEm;
}
Essa separação parece óbvia mas raramente acontece na prática. O benefício real: você consegue testar
EstoqueServicecom um mock sem tocar no HTTP, e consegue trocar a implementação doEstoqueClientsem mexer na lógica de negócio.
Platform Events: o que ninguém fala sobre ordem
Platform Events não garantem ordem de entrega. Se você publica três eventos para o mesmo registro em sequência rápida, o consumidor pode recebê-los fora de ordem.
Para a maioria dos casos isso não importa. Mas se você está sincronizando estado — especialmente transições de status — isso quebra.
O problema de ordem em Platform Events não aparece em testes. Aparece em produção, num dia de alto volume, quando dois eventos chegam invertidos e o registro fica num estado impossível.
A solução que uso: incluir um Timestamp__c no payload e ignorar eventos mais antigos que o último processado.
public with sharing class PedidoEventHandler {
public static void processar(List<Pedido_Atualizado__e> eventos) {
Map<Id, PedidoSync__c> ultimoProcessado = carregarUltimoProcessado(eventos);
for (Pedido_Atualizado__e evt : eventos) {
PedidoSync__c ultimo = ultimoProcessado.get(evt.PedidoId__c);
if (ultimo != null && evt.Timestamp__c <= ultimo.UltimoTimestamp__c) {
continue; // evento desatualizado, ignora
}
processarEvento(evt);
}
}
}
Retry: o que o Salesforce faz e o que você precisa fazer
Platform Events têm retry automático por 72 horas se o subscriber falhar. Isso parece suficiente — mas não é para todos os cenários.
O retry automático não distingue falha transitória (API externa fora do ar por 2 minutos) de falha permanente (payload inválido que sempre vai falhar). Sem tratamento explícito, você pode ficar retentando um evento para sempre até a janela expirar.
public with sharing class PedidoEventHandler {
private static final Integer MAX_TENTATIVAS = 3;
public static void processar(List<Pedido_Atualizado__e> eventos) {
List<IntegracaoLog__c> logs = new List<IntegracaoLog__c>();
for (Pedido_Atualizado__e evt : eventos) {
try {
enviarParaSistemaExterno(evt);
logs.add(logSucesso(evt));
} catch (EstoqueException e) {
Integer tentativas = obterTentativas(evt.PedidoId__c);
if (tentativas >= MAX_TENTATIVAS) {
logs.add(logFalhaDefinitiva(evt, e.getMessage()));
notificarEquipe(evt, e);
} else {
logs.add(logTentativa(evt, tentativas + 1));
}
}
}
if (!logs.isEmpty()) {
insert logs;
}
}
}
Named Credentials e o erro que sempre acontece em go-live
Toda integração usa endpoint hardcoded em sandbox e Named Credential em produção — ou deveria. O problema que vejo com frequência: alguém testa com Named Credential configurada para sandbox e esquece de criar a equivalente para produção. O deploy vai, a Named Credential não existe, e a integração explode no primeiro uso.
Checklist antes de qualquer go-live de integração:
- Named Credential criada em produção com URL correta
- Custom Setting ou Custom Metadata com parâmetros de timeout revisados para produção
- Mock configurado para testes (sem isso,
@isTest+ chamada HTTP = erro) - Permissão de callout concedida ao usuário de integração
O que ainda pode dar errado
Mesmo com a estrutura correta, há armadilhas que aparecem depois do go-live.
Limites de governor em contextos inesperados. O EstoqueClient chamado de um trigger via Future tem um limite de callout. Chamado de um Batch tem outro. Se o mesmo client for reutilizado em diferentes contextos, o comportamento de limite muda — e o erro que aparece não é óbvio.
Mocks mal configurados que escondem bugs reais. É comum criar um mock que sempre retorna sucesso para passar nos testes. O problema: o mock não testa o comportamento do client em falha, timeout ou resposta malformada. Testes que cobrem só o caminho feliz dão falsa segurança.
Mudança de Named Credential em produção sem aviso. Se as credenciais do sistema externo rotacionam (o que é boa prática de segurança), a Named Credential precisa ser atualizada manualmente. Não há alertas nativos para credencial expirada — a integração simplesmente começa a falhar com 401.
Deserialização silenciosa de campos opcionais. JSON.deserialize em Apex não lança exceção para campos ausentes — eles ficam nulos. Se um campo que você trata como obrigatório vier ausente numa versão nova da API, o código não quebra na linha do deserialize; quebra mais tarde, quando você tenta usar o valor nulo.
Conclusão prática
- Se você está estruturando uma nova integração → separe Service, Client e DTO desde o primeiro commit; refatorar isso depois é mais trabalhoso do que parece
- Se você usa Platform Events para sincronizar estado → implemente controle de ordem com
Timestamp__cantes de ir para produção - Se o retry automático do Salesforce é o único mecanismo de resiliência → adicione controle de tentativas explícito para distinguir falhas transitórias de permanentes
- Se o go-live está próximo → valide Named Credentials em produção com uma chamada de teste antes de liberar para usuários
No último post da série falo sobre monitoramento — porque descobrir que a integração está falhando pelo telefone do cliente não é uma estratégia.
Tem uma estrutura diferente que funciona melhor para você? Me conta no LinkedIn.