Uma caixa de entrada, dois provedores: onde colocar a fronteira
2 de junho de 2026 · Time Orion
Nosso painel atende conversas que chegam por dois caminhos diferentes de mensageria: um canal oficial, com API documentada, e um canal não oficial, com conexão persistente própria.
Eles são incompatíveis em quase tudo que importa:
| Oficial | Não oficial | |
|---|---|---|
| Conexão | requisição HTTP | socket persistente, um processo por conta |
| Janela para iniciar conversa | existe, e é limitada | não existe |
| Mensagem pré-aprovada | obrigatória fora da janela | não se aplica |
| Grupos | não expostos | disponíveis |
| Mídia | upload prévio, envio por identificador | bytes direto |
| Histórico ao conectar | não vem | vem |
E, mesmo assim, quem usa o painel vê uma caixa de entrada só. Este post é sobre onde a fronteira entre os dois foi colocada — e por quê.
O que não podia variar
Antes de decidir o que abstrair, decidimos o que nunca muda entre provedores:
conversa, mensagem, atribuição de responsável, notificação, histórico e controle de acesso.
Essas coisas são do nosso domínio, não do provedor. Se um dia entrar um terceiro canal, elas continuam iguais — e é justamente por isso que a tabela central de canais nasceu com nome genérico e uma coluna de "família". Nomear a tabela com o nome do primeiro provedor teria custado uma migração de renomeação no dia em que o segundo chegasse.
O que variou
O que muda entre provedores é como se envia e quais regras se aplicam. Isso ficou atrás de uma interface — um contrato com as operações que todo canal precisa oferecer.
E ficou uma decisão que vale destacar: as capacidades de cada provedor vivem em código, não em configuração.
Era tentador guardá-las numa tabela: "este canal suporta grupos", "aquele exige mensagem pré-aprovada". Seria flexível e seria errado. Capacidade não é preferência de operação — é propriedade de quem implementou a integração. Um administrador não pode "ligar" suporte a grupos num provedor que não expõe grupos; ele só conseguiria criar um estado impossível.
O corolário está no código: a função que resolve as capacidades de um provedor lança quando recebe um provedor desconhecido, em vez de devolver um padrão. Assim, o portão que decide se pode enviar mensagem fora da janela falha fechado. Um padrão permissivo teria permitido envio indevido; um padrão restritivo teria quebrado o canal em silêncio. Lançar é a única opção que grita.
O espaço de identificadores
Um problema prático interessante: os dois provedores geram identificadores de mensagem em espaços diferentes. O do canal oficial é global; o do não oficial só é único dentro de cada conversa.
A tabela tem uma restrição de unicidade global — que é o que garante a deduplicação de reentrega e o casamento com os eventos de status.
A solução foi assimétrica de propósito: o identificador do canal oficial entra intocado, e o do não oficial entra prefixado com a identificação do canal e da origem. Reescrever o identificador do primeiro teria desmontado o vínculo com os eventos de status e com as citações já gravadas — dado histórico que já existia.
A lição: quando um espaço de identificadores precisa acomodar outro, quem chega depois é quem se adapta.
O que não virou linha
Estado volátil — presença, "digitando", estado da conexão, batimento — não vira registro. Ele atualiza o estado atual do canal e pronto.
São dezenas de milhares de eventos por dia que não respondem a nenhuma pergunta futura. O mesmo raciocínio vale para a sincronização de histórico ao conectar uma conta: ela chega em lotes, e o receptor precisa estar preparado para processar um lote de centenas de mensagens numa requisição, em vez de uma requisição por mensagem.
A regra que ficou
Abstraia o que muda entre implementações. Mantenha concreto o que é do seu domínio.
Abstrair demais produz uma camada que precisa ser atualizada a cada provedor novo — o oposto do objetivo. Abstrair de menos produz condicionais espalhadas pela aplicação inteira.
O teste prático: quando o terceiro provedor chegar, quantos arquivos precisam mudar? Se a resposta for "um, mais o registro de provedores", a fronteira está no lugar certo.
O resumo
Uma caixa de entrada com dois provedores incompatíveis é possível quando a fronteira separa regra de envio (que varia) de modelo de conversa (que não varia) — e quando capacidade é propriedade do código, com falha fechada para o desconhecido.
O que o usuário vê é uma tela só. O que sustenta isso é a lista, feita antes, do que jamais poderia variar.