A fonte: duas APIs públicas
As duas casas legislativas do Brasil publicam dados abertos. A Câmara dos Deputados expõe coleções como deputados, partidos, órgãos, proposições e votações, com documentação interativa em Swagger e uma coleção que lista as atualizações recentes. O Senado Federal tem seu próprio serviço de dados abertos, com estrutura diferente.
Para o Como Votam, construí um pipeline automatizado que coleta das duas e mantém nosso banco atualizado, com orçamento de ONG. Este artigo reúne as lições de design que valem para qualquer pipeline sobre dados públicos, sem entrar nos detalhes internos do projeto.
Duas fontes, um modelo
A Câmara e o Senado descrevem coisas parecidas (pessoas, partidos, matérias, votos) em formatos diferentes. A primeira decisão é onde a diferença mora. Mantenha cada fonte atrás de seu próprio adaptador, que fala o formato dela e entrega um modelo interno comum. Tudo o que vem depois, do armazenamento à análise, não sabe de qual casa o dado veio.
Essa única fronteira se paga toda vez que uma fonte muda o nome de um campo ou um endpoint.
Faça toda carga idempotente
Um pipeline agendado, mais cedo ou mais tarde, vai rodar duas vezes, ou quebrar no meio e rodar de novo. A única regra sensata é que repetir uma carga produza o mesmo resultado que rodá-la uma vez.
Em um banco relacional, isso normalmente significa upserts por um identificador estável da fonte:
INSERT INTO votes (source, source_id, matter_id, choice, voted_at)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (source, source_id)
DO UPDATE SET
choice = EXCLUDED.choice,
voted_at = EXCLUDED.voted_at;O trecho é ilustrativo. Repare na chave composta: as duas casas podem reutilizar os mesmos identificadores numéricos, então a fonte faz parte da chave.
Sincronize de forma incremental, usando o sinal da própria fonte
Baixar tudo de novo a cada execução é lento e deselegante com um serviço público. Quando a fonte informa o que mudou, use isso. Os dados abertos da Câmara incluem uma coleção de atualizações exatamente para esse fim: pergunte o que mudou em uma janela de tempo e busque só esses registros.
Dois hábitos tornam a sincronização incremental segura:
- Sobreponha as janelas. Peça um pouco mais do que o tempo desde a última execução e conte com cargas idempotentes para absorver as duplicatas.
- Grave o cursor só depois que a carga der certo. Se uma execução falhar, a próxima recomeça do mesmo ponto.
Reconcilie, porque a sincronização pode derivar
A sincronização incremental é eficiente, e também pode derivar em silêncio: uma atualização perdida, um registro corrigido na origem sem aviso, um bug do seu lado. A resposta é uma etapa de reconciliação separada, que compara periodicamente o que você tem com o que a fonte informa (contagens, totais, uma amostra de registros) e sinaliza diferenças.
Manter sincronização e reconciliação como preocupações distintas importa. A sincronização é otimizada para velocidade. A reconciliação é otimizada para confiança, e é ela que permite dizer aos usuários, com honestidade, que os números batem com o registro oficial.
Seja um bom cidadão de uma API pública
Serviços públicos têm limites que talvez você não veja na documentação. Limite a concorrência, faça recuo em caso de erro, identifique bem o seu cliente e guarde em cache o que não muda. Isso mantém o pipeline confiável e o serviço disponível para todos.
Resumo
- Isole cada fonte atrás de um adaptador e de um modelo comum.
- Cargas idempotentes tornam a falha entediante.
- Use o feed de mudanças da fonte, sobreponha as janelas e grave o cursor por último.
- Reconcilie separadamente, porque a confiança precisa de uma verificação própria.


