ADR-003: ETL Architecture
Estado
Section titled “Estado”Aceptada - 2024 (ETL service implementado en Python)
Contexto
Section titled “Contexto”El sistema Nostromo requiere ingesta automática de datos desde SII (Servicio de Impuestos Internos de Chile):
- Datos necesarios: Indicadores económicos (UF, UTM, IPC), tablas paramétricas (tramos impuestos, tasas previsionales)
- Fuente: Páginas web SII, PDFs publicados, APIs limitadas
- Frecuencia: Mensual/trimestral para parámetros, diario para indicadores
- Formato destino: PostgreSQL (schema
parametros)
Desafíos técnicos
Section titled “Desafíos técnicos”- Web scraping: SII publica datos en HTML tables, PDFs, no tiene API completa
- Parsing complejo: Tablas con formato irregular, colspan/rowspan, footnotes
- Transformación: Datos crudos → formato normalizado para base de datos
- Manejo de errores: SII cambia estructura de páginas sin previo aviso
- Scheduling: ETL debe correr automáticamente según calendario
Decisión
Section titled “Decisión”Implementar servicio ETL separado en Python, NO integrado en Orchestrator:
Arquitectura
Section titled “Arquitectura”[SII Websites/PDFs] ↓[Python ETL Service] - BeautifulSoup (HTML parsing) - Pandas (data transformation) - Schedule (cron-like) ↓[PostgreSQL - schema: parametros] ↓[Orchestrator - CommonDataService] ↓[Sevastopol Frontend]Tecnologías
Section titled “Tecnologías”Python 3.11+:
- BeautifulSoup4: HTML parsing
- Requests: HTTP client
- Pandas: Data transformation
- psycopg2: PostgreSQL driver
- Schedule: Task scheduling
Deployment:
- Container separado (Docker)
- Cron jobs diarios (indicadores) y mensuales (parámetros)
Responsabilidades
Section titled “Responsabilidades”ETL Service:
- Scrape SII websites
- Parse tables/PDFs
- Transform to normalized format
- Insert/update PostgreSQL
Orchestrator (CommonDataService):
- READ-ONLY access a datos ETL
- Expone datos vía API endpoints
- NO modifica datos de parámetros
Consecuencias
Section titled “Consecuencias”Positivas
Section titled “Positivas”✅ Python ecosystem para ETL
- BeautifulSoup: Mejor HTML parser que alternativas Node.js
- Pandas: Transformación de datos tabular (mejor que DataFrames JS)
- Ecosystem maduro: Librerías para PDF parsing, OCR si se requiere
✅ Separation of concerns
- ETL no afecta latency de Orchestrator (requests HTTP)
- Si ETL crashea, Orchestrator sigue funcionando (usa datos cached)
- Deploy independiente: update ETL sin restart Orchestrator
✅ Scheduling simplificado
- Cron jobs en container ETL
- No contamina codebase Orchestrator con logic ETL
- Fácil debuggear logs ETL aislados
✅ Error handling especializado
- ETL puede retry múltiples veces sin afectar users
- Alertas específicas si scraping falla (SII cambió estructura)
- No afecta SLA de Orchestrator API
✅ Performance
- ETL corre offline (no en critical path de requests)
- Orchestrator solo lee datos pre-procesados (queries rápidas)
Negativas (Trade-offs)
Section titled “Negativas (Trade-offs)”❌ Servicio adicional que mantener
- Otro container, otro codebase, otro lenguaje
- Deploy más complejo (Python deps, virtual env)
- Mitigación: Containerización simplifica deploy
❌ Comunicación inter-servicio
- ETL escribe a PostgreSQL, Orchestrator lee
- Si ETL falla silenciosamente, datos quedan desactualizados
- Mitigación: Monitoring de timestamp última actualización, alertas si datos > 7 días
❌ Duplicación de lógica DB
- ETL tiene conexión PostgreSQL propia
- Orchestrator también tiene pool PostgreSQL
- Mitigación: Acceptable trade-off, cada servicio tiene propósito diferente
❌ Barrera de entrada para equipo
- Developers deben conocer Python + Node.js
- Onboarding más complejo
- Mitigación: ETL es simple, ~500-1000 líneas, fácil de aprender
❌ No real-time
- Datos actualizados en batch (diario/mensual)
- Si SII publica UF nueva, puede tardar 24h en reflejarse
- Mitigación: Para caso de uso actual (nóminas mensuales), delay es acceptable
Alternativas Consideradas
Section titled “Alternativas Consideradas”Opción A: ETL integrado en Orchestrator (Node.js)
Section titled “Opción A: ETL integrado en Orchestrator (Node.js)”Descripción: Escribir lógica ETL en TypeScript, correr en Orchestrator.
Rechazada porque:
- Ecosystem Node.js pobre para scraping: Cheerio es limitado vs BeautifulSoup
- Performance: Node.js single-threaded, scraping bloquearía event loop
- Pandas no existe en JS: Alternativas (danfojs, Data-Forge) son inmaduras
- Contamina codebase: Orchestrator es API backend, no ETL tool
Cuándo sería válida: Si datos vienen de API REST estructurada (no scraping), o volumen es muy pequeño (1-2 endpoints).
Opción B: Serverless ETL (AWS Lambda / Cloud Functions)
Section titled “Opción B: Serverless ETL (AWS Lambda / Cloud Functions)”Descripción: ETL como funciones serverless en cloud.
Rechazada porque:
- Cold start: Lambdas tienen ~1-3s cold start, inaceptable para scraping time-sensitive
- Timeout limits: AWS Lambda max 15 min, algunos ETL jobs pueden ser más largos
- Costo: Facturación por invocación, para jobs diarios puede ser caro
- Vendor lock-in: Código específico a AWS/GCP
Cuándo sería válida: Si infraestructura es 100% cloud, o ETL corre muy esporádicamente (una vez al mes).
Opción C: Manual data entry
Section titled “Opción C: Manual data entry”Descripción: Empleados actualizan parámetros manualmente en admin panel.
Rechazada porque:
- Error-prone: Humanos cometen errores al copiar tablas
- No escalable: Cada mes, empleado debe re-ingresar 100+ parámetros
- Delay: Depende de disponibilidad humana, puede tardar días
- No viable para startup lean
Cuándo sería válida: Si parámetros cambian raramente (una vez al año), o hay equipo grande de data entry.
Opción D: Usar API de terceros (ej: InvestChile API)
Section titled “Opción D: Usar API de terceros (ej: InvestChile API)”Descripción: Pagar servicio que ya expone datos SII vía API REST.
Considerada pero rechazada:
- Costo recurrente: APIs de terceros cobran mensualmente
- Dependencia externa: Si servicio cae, Nostromo no funciona
- No existe API completa: APIs existentes no tienen TODOS los parámetros que Nostromo necesita
- SII data es pública: No tiene sentido pagar por datos que son gratuitos
Cuándo sería válida: Si presupuesto permite, y API cubre 100% de datos requeridos.
Referencias
Section titled “Referencias”Implementación:
- ETL Service - Codebase Python (si existe en Nostromo repo)
- CommonDataService - READ de parámetros
- Schema parametros - Estructura DB
Decisiones relacionadas:
- Orchestrator Services - CommonDataService docs
- Mother Container - PostgreSQL setup
Referencias externas:
- BeautifulSoup Documentation
- Pandas Documentation
- SII Website - Fuente de datos
Notas Adicionales
Section titled “Notas Adicionales”Datos scrapeados
Section titled “Datos scrapeados”Indicadores diarios:
- UF (Unidad de Fomento)
- UTM (Unidad Tributaria Mensual)
- IPC (Índice de Precios al Consumidor)
Parámetros mensuales/anuales:
- Tramos impuestos (escalas PPM, global complementario)
- Tasas previsionales (AFP, Isapre, Fonasa)
- Topes imponibles
- Sueldo mínimo
Scheduling
Section titled “Scheduling”Cron schedule:
# Indicadores diarios (UF, UTM) - Run at 8 AM0 8 * * * python etl/daily_indicators.py
# Parámetros mensuales - Run 1st of month0 9 1 * * python etl/monthly_params.py
# Parámetros anuales - Run Jan 150 10 15 1 * python etl/annual_params.pyError handling
Section titled “Error handling”Estrategias:
- Retry con backoff: Si scraping falla, retry 3 veces con delay exponencial
- Alertas: Slack/email si ETL falla después de retries
- Fallback: Si datos nuevos no se obtienen, usar últimos datos disponibles
- Manual override: Admin panel permite upload manual de parámetros si ETL falla
Monitoring
Section titled “Monitoring”Métricas:
- Timestamp última actualización (por tipo de dato)
- Success rate de ETL jobs (%)
- Latency de scraping (tiempo de ejecución)
Alertas:
- Si
last_updated > 7 days→ CRITICAL - Si ETL job falla 3 veces consecutivas → HIGH
Lecciones aprendidas
Section titled “Lecciones aprendidas”-
SII cambia estructura sin aviso: ETL debe ser resiliente a cambios HTML
- Solución: Tests unitarios con HTML samples, fail gracefully si estructura cambia
-
PDFs son complicados: Algunos parámetros solo vienen en PDF
- Solución: Usar tabula-py (PDF table extractor), fallback a manual entry si falla
-
Rate limiting: SII puede bloquear si scrapeamos muy rápido
- Solución: Delays entre requests (2-3s), user-agent headers
Futuro
Section titled “Futuro”Mejoras planeadas:
- Cacheo de HTML: Guardar HTML scrapeado para debugging posterior
- Diff detection: Alertar si datos cambian significativamente (ej: UF sube 50% → probablemente error)
- API SII oficial: Si SII lanza API, migrar de scraping a API calls