O problema
Gestão de iluminação pública, no modelo tradicional, é reativa: o cidadão liga reclamando de uma rua
escura, alguém anota num caderno, uma equipe sai para verificar. Não há inventário confiável do parque,
não há como provar consumo, e não há histórico de manutenção. O contrato com o município, porém, exige
exatamente isso — indicadores, prazos de atendimento e prestação de contas.
O sistema precisava resolver quatro coisas ao mesmo tempo, cada uma com requisitos de tempo
muito diferentes: telemetria contínua dos controladores, operação de campo
por equipes com conectividade instável, atendimento ao cidadão em canal assíncrono
(WhatsApp), e relatórios pesados que podem levar minutos para renderizar. Misturar tudo
num único fluxo síncrono era garantia de derrubar a API.
Arquitetura
A separação central é entre o caminho quente (telemetria e comandos, que precisam ser rápidos e nunca
bloquear) e o caminho frio (relatórios e processamento em lote, que podem demorar desde que não travem
ninguém). Filas fazem a fronteira entre os dois.
O que o sistema entrega hoje
- Mapa operacional com carregamento por área visível: os pontos são buscados por bounding box, com
debounce e limite de zoom, para não trazer o parque inteiro a cada arrasto do mapa.
- Ordens de serviço com ciclo de vida completo, vinculadas a equipes, com registro de conclusão
e localização.
- Atendimento via WhatsApp com contagem correta de conversas não lidas — depois de reescrever o
contador para considerar conversas distintas em vez de mensagens, o que inflava o badge com
sessões de bot e sessões órfãs.
- Relatórios per-tenant com identidade visual de cada empresa aplicada na renderização.
- Logging estruturado, índices de banco revisados e análise estática contínua no pipeline.
O que eu faria diferente
A troca de empresa no painel web passou por três implementações antes de estabilizar. A causa raiz era
conhecida — a função de recuperação do usuário devolvia uma nova referência de objeto a cada chamada,
realimentando o efeito que dependia dela — e a solução final foi um recarregamento explícito da página.
Funciona, é previsível, e é honestamente uma capitulação: o correto teria sido memoizar o contexto de
autenticação desde o início, em vez de tratar o sintoma três vezes.
A segunda lição foi sobre backfill. A migração multi-tenant assumiu que os dados históricos já tinham
tenant. Não tinham: postes, pontos e registros de consumo estavam todos com company_id nulo,
e só as ordens de serviço estavam corretas. Verificar o estado real da produção antes de escrever a
migration teria evitado o susto.