Blog

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.