Visão Geral da Arquitetura¶
Resumo¶
O DestaquesGovbr é uma plataforma de agregação e enriquecimento de notícias governamentais composta por 8 camadas principais, com arquitetura event-driven e pipeline Medallion:
- Coleta - Raspagem automatizada de ~160 sites gov.br via Scraper API
- Armazenamento Medallion - Bronze (GCS Parquet) → Silver (PostgreSQL) → Gold (BigQuery + Feature Store)
- Enriquecimento - Classificação temática e sumarização via AWS Bedrock (Claude 3 Haiku) com workers event-driven
- Embeddings - Geração de vetores 768-dim para busca semântica via Embeddings API (worker event-driven)
- Indexação - Typesense para busca full-text e vetorial (atualizado via workers Pub/Sub)
- Distribuição - HuggingFace para dados abertos + Federação ActivityPub (Mastodon/Misskey)
- Apresentação - Portal Next.js, Streamlit App e Bots (Telegram)
- Fachada de Dados - GraphQL API unificada entre consumidores e backends
Fachada GraphQL (migração R1, 2026)
Os consumidores (portal e, progressivamente, os workers) deixaram de acessar
Firestore/PostgreSQL/Typesense diretamente e passaram a usar uma fachada
GraphQL única — o serviço graphql-api. Isso
desacopla os consumidores do formato dos backends e centraliza a
autenticação. Ver ADR-002. O pipeline de
ingestão (camadas 1–6) segue acessando o PostgreSQL diretamente; migrar
os workers para a fachada é um passo de desacoplamento em andamento.
Mudança Arquitetural (Fev-Mar 2026): Pipeline migrado de batch (DAGs cron) para event-driven (Pub/Sub), reduzindo latência de ~45 min para ~15 segundos.
Diagrama de Arquitetura (Atualizado v1.1)¶
flowchart TB
subgraph COLETA["1. Coleta"]
A[160+ Sites gov.br] -->|Scraping| B[Scraper API<br/>Cloud Run]
end
subgraph MEDALLION["2. Armazenamento Medallion"]
direction LR
BRONZE[(Bronze<br/>GCS Parquet)]
SILVER[(Silver<br/>PostgreSQL<br/>Cloud SQL)]
GOLD[(Gold<br/>BigQuery<br/>+ Feature Store)]
end
subgraph ENRIQUECIMENTO["3. Enriquecimento AI (Event-Driven)"]
D[AWS Bedrock<br/>Claude 3 Haiku]
EW[Enrichment Worker<br/>Cloud Run]
EW -->|LLM Inference| D
D -->|Themes + Summary<br/>+ Sentiment + Entities| EW
end
subgraph EMBEDDINGS["4. Embeddings (Event-Driven)"]
H[Embeddings API<br/>Cloud Run Worker]
H -->|Modelo Local<br/>768-dim| H
end
subgraph INDEXACAO["5. Indexação - Busca"]
I[(Typesense<br/>VM)]
TSW[Typesense Sync<br/>Worker Cloud Run]
end
subgraph DISTRIBUICAO["6. Distribuição"]
J[(HuggingFace<br/>Dados Abertos)]
AP[ActivityPub Server<br/>Cloud Run]
MASTO[Mastodon<br/>Misskey]
end
subgraph APRESENTACAO["7. Apresentação"]
K[Portal Next.js<br/>Cloud Run]
L[Streamlit App<br/>HF Spaces]
TG[Telegram Bot]
end
subgraph PUBSUB["Event Mesh (Cloud Pub/Sub)"]
T1{{dgb.news.scraped}}
T2{{dgb.news.enriched}}
T3{{dgb.news.embedded}}
end
B -->|INSERT + publish| SILVER
B -->|publish| T1
T1 -->|push| EW
EW -->|UPDATE + publish| SILVER
EW -->|publish| T2
T2 -->|push| H
H -->|UPDATE + publish| SILVER
H -->|publish| T3
T2 -->|push| TSW
T3 -->|push| TSW
TSW -->|fetch + upsert| I
T2 -->|push| AP
AP -.->|federation| MASTO
SILVER -->|DAG export| BRONZE
SILVER -->|DAG sync| GOLD
SILVER -->|DAG sync| J
I -->|API Search| K
J -->|Análise| L
SILVER -->|Webhook| TG
style COLETA fill:#e3f2fd
style MEDALLION fill:#e8f5e9
style ENRIQUECIMENTO fill:#fff3e0
style EMBEDDINGS fill:#fff8e1
style INDEXACAO fill:#fce4ec
style DISTRIBUICAO fill:#e1f5fe
style APRESENTACAO fill:#f3e5f5
style PUBSUB fill:#f3e5f5,stroke:#9C27B0,stroke-width:2px
Componentes por Camada¶
1. Coleta (scraper repo)¶
| Componente | Responsabilidade |
|---|---|
| Scraper API (Cloud Run) | API REST que expõe endpoints /scrape para cada agência |
| WebScraper | Raspagem genérica de sites gov.br |
| EBCScraper | Raspagem especializada da EBC |
| PostgresManager | Insert no PostgreSQL + publicação de eventos Pub/Sub |
| EventPublisher | Publica evento dgb.news.scraped após INSERT |
Trigger: DAGs Airflow (cron 15 min) chamam POST /scrape por agência
Dados extraídos por notícia:
| Campo | Descrição |
|---|---|
unique_id |
Hash MD5 (agency + published_at + title) |
agency_key |
Identificador do órgão (ex: fazenda, educacao) |
published_at |
Data/hora de publicação (ISO 8601, UTC) |
updated_datetime |
Data/hora de atualização, quando disponível |
scraped_at |
Data/hora da extração |
title |
Título da notícia |
subtitle |
Subtítulo (quando disponível) |
editorial_lead |
Lead editorial / linha fina |
url |
URL original da notícia |
content |
Conteúdo completo em Markdown |
thumbnail_url |
URL da imagem principal |
video_url |
URL de vídeo incorporado (quando disponível) |
category |
Categoria original do site |
tags |
Tags/keywords do site |
2. Armazenamento Medallion¶
Arquitetura de 3 camadas (ADR-001):
Bronze Layer (Dados Brutos)¶
- Localização: Google Cloud Storage bucket
dgb-data-lake/bronze/ - Formato: Parquet particionado por data
- Lifecycle: Standard → Nearline (90d) → Coldline (365d)
- BigQuery: External tables sobre GCS
- Uso: Auditoria, reprocessamento, data lineage
Silver Layer (Dados Limpos)¶
- Instância: Cloud SQL
destaquesgovbr-postgres(PostgreSQL 15) - Tabelas principais:
news- 300k+ notícias normalizadas e enriquecidasnews_features- Feature Store JSONB (temas, sentiment, entities, embeddings)agencies- 156 órgãos governamentaisthemes- 200+ temas (3 níveis hierárquicos)- Uso: Fonte de verdade transacional (OLTP)
→ Veja detalhes em postgresql.md
Gold Layer (Dados Agregados)¶
- BigQuery: Dataset
dgb_analytics - Conteúdo: Features agregadas, analytics, pageviews (Umami), métricas editoriais
- Uso: OLAP, dashboards, análises avançadas
3. Enriquecimento (data-science repo)¶
Arquitetura Event-Driven:
| Componente | Tecnologia | Responsabilidade |
|---|---|---|
| Enrichment Worker | Cloud Run (FastAPI) | Recebe eventos dgb.news.scraped, classifica via Bedrock, publica dgb.news.enriched |
| AWS Bedrock | Claude 3 Haiku | LLM para classificação temática + resumo + sentiment + entity extraction |
| NewsClassifier | Python class | Prompts e parsing de resposta Bedrock |
| PostgresManager | Python | Update de temas e features no PostgreSQL |
| PubSubPublisher | Python | Publica eventos enriquecidos |
Fluxo:
- Recebe push de
dgb.news.scraped(unique_id) - Verifica idempotência (
most_specific_theme_id IS NOT NULL→ skip) - Busca artigo do PostgreSQL
- Classifica via Bedrock (temas L1/L2/L3 + resumo + sentiment + entities)
- Atualiza PostgreSQL (campos
theme_*,summary+news_features.featuresJSONB) - Publica
dgb.news.enriched
Campos enriquecidos:
theme_1_level_1_code/label- Tema nível 1 (ex: "01 - Economia")theme_1_level_2_code/label- Tema nível 2 (ex: "01.01 - Política Econômica")theme_1_level_3_code/label- Tema nível 3 (ex: "01.01.01 - Política Fiscal")most_specific_theme_code/label- Tema mais específico disponívelsummary- Resumo gerado por LLMnews_features.features(JSONB):sentiment: {positive/neutral/negative, score}entities: {pessoas, organizações, locais}- Extensível sem DDL
Migração Cogfy → Bedrock (27/02/2026):
| Aspecto | Cogfy (anterior) | AWS Bedrock (atual) |
|---|---|---|
| Latência | Batch 20 min (1000 registros) | ~5s por notícia (event-driven) |
| Custo | Alto (SaaS) | ↓ 40% (pay-per-token) |
| Controle | Prompts fixos | Controle total de prompts |
| Features | Temas + resumo | Temas + resumo + sentiment + entities |
4. Embeddings (embeddings repo)¶
Arquitetura Event-Driven:
| Componente | Tecnologia | Responsabilidade |
|---|---|---|
| Embeddings API | Cloud Run (FastAPI) | Worker Pub/Sub + endpoint /generate público |
| Modelo | paraphrase-multilingual-mpnet-base-v2 |
768-dim, otimizado para português |
/process endpoint |
FastAPI | Recebe dgb.news.enriched, gera embedding, publica dgb.news.embedded |
Fluxo:
- Recebe push de
dgb.news.enriched - Verifica idempotência (
content_embedding IS NOT NULL→ skip) - Busca
title + summary + contentdo PostgreSQL - Prepara texto via
prepare_text_for_embedding() - Gera embedding 768-dim (modelo local, sem HTTP hop)
- Atualiza
content_embeddingno PostgreSQL - Publica
dgb.news.embedded
Características:
- Vetores de 768 dimensões
- Input:
title + summary(fallback paracontent) - Armazenados em
news.content_embedding(tipoVECTORpgvector) - Modelo carregado em memória (evita HTTP overhead)
5. Indexação (Typesense)¶
Arquitetura Event-Driven:
| Componente | Tecnologia | Responsabilidade |
|---|---|---|
| Typesense | VM Compute Engine | Motor de busca full-text + vetorial |
| Typesense Sync Worker | Cloud Run (FastAPI) | Recebe eventos enriched + embedded, faz upsert |
| TypesenseClient | Python | Conexão com Typesense |
Fluxo:
- Recebe push de
dgb.news.enrichedOUdgb.news.embedded - Busca documento completo do PostgreSQL
- Prepara documento via
prepare_document()(inclui temas, summary, embedding) - Upsert no Typesense (idempotente)
Collection: news
Configurado para:
- Busca full-text em
title,subtitle,content - Busca vetorial via
content_embedding(768-dim, HNSW index) - Filtros facetados por
agency_key,theme_l1,theme_l2,theme_l3,published_at - Ordenação por relevância, data e score de priorização
Latência de Atualização: ~15 segundos (scraping → indexação) vs ~45 minutos (batch anterior)
6. Distribuição¶
HuggingFace¶
Dataset principal: nitaibezerra/govbrnews
- ~300.000+ documentos
- Sincronização diária via DAG Airflow
sync_postgres_to_huggingface(6 AM UTC) - Abordagem incremental (parquet shards)
- Versionamento automático pelo HuggingFace
→ Veja detalhes em workflows/airflow-dags.md
Federação ActivityPub (activitypub-server repo)¶
Componente: ActivityPub Server (Cloud Run, FastAPI)
Fluxo Event-Driven:
- Recebe push de
dgb.news.enriched - Busca artigo do PostgreSQL
- Cria
NoteActivityPub (formato Mastodon/Misskey) - Publica na outbox do servidor
- Distribui para followers via protocolo ActivityPub
Protocolos: ActivityPub (W3C), WebFinger, HTTP Signatures
Interoperabilidade: Mastodon, Misskey, Pixelfed, outros servidores federados
Usuário: @destaques@activitypub.destaquesgovbr.gov.br (exemplo)
7. Apresentação¶
| App | Tecnologia | URL | Descrição |
|---|---|---|---|
| Portal | Next.js 15 (busca via Typesense; clippings/marketplace/push/widgets via GraphQL) | portal-klvx64dufq-rj.a.run.app | Interface web principal com busca, clippings, widgets |
| Streamlit | Python + Altair | HuggingFace Spaces | Análises exploratórias sobre o dataset |
| Telegram Bot | Python + Aiogram | - | Bot para scraping sob demanda e alertas |
→ Detalhes do portal em ../modulos/portal.md
8. Fachada de Dados (graphql-api)¶
Camada introduzida na migração R1 (2026). Um serviço Cloud Run (Strawberry GraphQL + FastAPI) que expõe um schema tipado único e intermedeia o acesso aos backends, em vez de cada consumidor falar direto com Firestore/PostgreSQL/Typesense.
flowchart LR
portal[Portal Next.js] -->|HTTP + JWT| api
workers[Workers de dados<br/>migração em andamento] -.->|HTTP + OIDC| api
embed[Widgets embarcáveis] -->|HTTP público| api
api[graphql-api<br/>Strawberry + FastAPI] --> fs[(Firestore)]
api --> pg[(PostgreSQL)]
api --> ts[(Typesense)]
- Portal: clippings, marketplace, push, widgets e busca via GraphQL (atrás de
feature flags
graphql.*); o agente de IA transmite por SSE (/graphql/stream). - Workers: têm uma superfície interna (
newsForTypesense,upsertFeatures, …) pronta para migrarem do acesso direto ao Postgres. - Detalhes, schema e exemplos: módulo GraphQL API e a documentação profunda do serviço.
Event Mesh (Cloud Pub/Sub)¶
Topics criados (infra Terraform):
| Topic | Publisher | Subscribers | Payload |
|---|---|---|---|
dgb.news.scraped |
Scraper API | Enrichment Worker, Typesense Sync | unique_id, agency_key, published_at, scraped_at |
dgb.news.enriched |
Enrichment Worker | Embeddings API, Typesense Sync, ActivityPub Server, Push Notifications | unique_id, enriched_at, most_specific_theme_code, has_summary |
dgb.news.embedded |
Embeddings API | Typesense Sync | unique_id, embedded_at, embedding_dim |
Dead-Letter Queues (DLQ): Cada topic tem DLQ correspondente para mensagens que falharam após máximo de tentativas.
Retry Policy: Exponential backoff (10s → 600s)
Authentication: OIDC tokens (Cloud Run → Pub/Sub)
→ Detalhes em pubsub-workers.md
Fluxo de Dados Event-Driven (Atual)¶
sequenceDiagram
participant Airflow as Airflow DAG (cron 15min)
participant Scraper as Scraper API
participant PG as PostgreSQL
participant PS1 as Pub/Sub: scraped
participant EW as Enrichment Worker
participant Bedrock as AWS Bedrock
participant PS2 as Pub/Sub: enriched
participant EAPI as Embeddings API
participant PS3 as Pub/Sub: embedded
participant TSW as Typesense Sync
participant TS as Typesense
participant Portal as Portal Next.js
Note over Airflow: Trigger 15 min
Airflow->>Scraper: POST /scrape (agency)
Scraper->>Scraper: scrape gov.br site
Scraper->>PG: INSERT artigos
Scraper->>PS1: publish scraped
PS1->>EW: push notification
EW->>PG: fetch article
EW->>Bedrock: classify (themes + summary + sentiment + entities)
Bedrock-->>EW: JSON response
EW->>PG: UPDATE themes + features
EW->>PS2: publish enriched
PS2->>EAPI: push notification
EAPI->>PG: fetch title + summary
EAPI->>EAPI: generate embedding 768-dim
EAPI->>PG: UPDATE content_embedding
EAPI->>PS3: publish embedded
PS2->>TSW: push notification (enriched)
PS3->>TSW: push notification (embedded)
TSW->>PG: fetch full document
TSW->>TS: upsert
Portal->>TS: search query
TS-->>Portal: results (<100ms)
Note over Scraper,Portal: Latência total: ~15 segundos
Fluxo Batch Complementar¶
Alguns processos ainda rodam em batch (DAGs Airflow) por natureza não-crítica:
sequenceDiagram
participant Airflow as Cloud Composer
participant PG as PostgreSQL
participant GCS as GCS (Bronze)
participant BQ as BigQuery (Gold)
participant HF as HuggingFace
Note over Airflow: DAG bronze_news_ingestion (diário 2 AM)
Airflow->>PG: query news table
Airflow->>GCS: export Parquet particionado
Note over Airflow: DAG sync_analytics_to_bigquery (diário 3 AM)
Airflow->>PG: query news_features
Airflow->>BQ: insert analytics
Note over Airflow: DAG sync_postgres_to_huggingface (diário 6 AM)
Airflow->>PG: query novos registros
Airflow->>HF: upload parquet shard
Note over HF: Portal e comunidade consomem dados atualizados
Tecnologias Principais¶
Backend (Data Platform)¶
- Python 3.11+ com Poetry
- PostgreSQL 15 (Cloud SQL) com psycopg2 e pgvector
- AWS Bedrock SDK (boto3) para Claude 3 Haiku
- Google Cloud Pub/Sub para event mesh
- FastAPI para workers Cloud Run
- BeautifulSoup4 para parsing HTML
- datasets + huggingface_hub para sync HF
- Apache Airflow 3 (Cloud Composer)
Frontend (Portal)¶
- Next.js 15 com App Router
- TypeScript 5
- Typesense para busca
- shadcn/ui + Tailwind CSS
- React Query para data fetching
- NextAuth.js v5 para autenticação
Infraestrutura¶
- GCP - Cloud Run (8 services), Compute Engine (Typesense), Cloud SQL, Cloud Composer, Cloud Pub/Sub, GCS, BigQuery
- AWS - Bedrock (Claude 3 Haiku)
- Terraform - IaC (infra repo)
- Docker - Containerização
- GitHub Actions - CI/CD
- Apache Airflow - Orquestração de pipelines batch
Custos Estimados (Atualizado Abr 2026)¶
| Componente | Custo/mês | Mudança |
|---|---|---|
| Cloud SQL (PostgreSQL) | ~$48 | - |
| Compute Engine (Typesense) | ~$64 | - |
| Cloud Run (8 services) | ~$25 | +$10 (workers) |
| Cloud Composer (Airflow) | ~$100-150 | - |
| Cloud Pub/Sub | ~$2 | +$2 (novo) |
| GCS (Bronze layer) | ~$2 | +$2 (novo) |
| BigQuery (Gold layer) | ~$2 | +$2 (novo) |
| AWS Bedrock (Claude Haiku) | ~$8-12 | -$30 (vs Cogfy) |
| Total | ~$250-305 | +$20-25 (+8%) |
Nota: Custo incremental justificado por: - Latência ↓ 99.97% (45min → 15s) - Custo LLM ↓ 40% (Bedrock vs Cogfy) - 4 features extras (sentiment, entities, embeddings near-real-time, federação) - Escalabilidade automática (workers scale-to-zero)
Próximos Passos¶
- [ ] Implementar reconciliação automática via DAG (safety net para eventos perdidos)
- [ ] Adicionar tracing distribuído (OpenTelemetry)
- [ ] Métricas de latência por worker (Prometheus + Grafana)
- [ ] Alertas proativos (Pub/Sub DLQ → notificação Slack)
- [ ] Gov.Br SSO em produção (aguardando domínio .gov.br)
Links Relacionados¶
→ fluxo-de-dados.md - Detalhes do pipeline completo → componentes-estruturantes.md - Árvore temática e órgãos → pubsub-workers.md - Arquitetura event-driven detalhada → adrs/adr-001-arquitetura-dados-medallion.md - Decisão arquitetural Medallion