ADR — Arquitectura de datos por capas (medallion + capa de control de gestión)
:::note Decisión Reorganizar la capa de datos de IngelCoding en 4 capas con propósito único y nombres descriptivos (medallion bronze/silver/gold + operacional), separando dentro de la capa analítica el modelo dimensional (para Power BI) de los indicadores de control de gestión (capa semántica materializada para análisis agéntico, portal y staging previo a promoción). Migración incremental, con valorizaciones como piloto. :::
Contexto (el problema)
Sección titulada «Contexto (el problema)»El inventario del estado actual (2026-06-13) reveló una capa de datos sin capas ni propósito claro:
ingeldata.dbes un cajón de sastre: mezcla tablas de dominio + logs de pipeline (pipeline_runs,publish_log) + metadata + versionado. Las escrituras de auditoría compiten con las de negocio.- Mismo dato en 3 lugares con conteos distintos: p. ej. facturas en
ingeldata.db(325),curated/facturacion.db(3) ydata_warehouse.db(2.584). No hay fuente única. - Nombres inconsistentes:
snake_caseen unas DB,Title_Casecon tildes (N°_Pedido) en otras → rompe PostgreSQL/ODBC y obliga a parches de normalización. - No existe capa de negocio: las métricas que dan valor (p. ej. la trazabilidad facturación↔valorización = 52% del monto reciente) se calculan con scripts ad-hoc, no como datos reutilizables y documentados.
- Sin sincronización garantizada entre
ingeldata.db(operacional) ydata_warehouse.db(analítico); pedidos no llegan al DW como hechos.
Un incidente concreto lo expuso: migrate_all --source ingeldata (auto-discover) intentaba DROP+CREATE de 22 tablas mezcladas, arriesgando 18 dominios de producción al migrar solo valorizaciones.
Decisión — 4 capas con nombres descriptivos
Sección titulada «Decisión — 4 capas con nombres descriptivos»Los tier medallion (bronze/silver/gold/meta) quedan como alias técnico en la documentación; las carpetas/archivos usan nombres descriptivos en español de negocio. Numeración para reflejar el flujo del dato.
01_recepcion/ (bronze) Parquets crudos de correos IMAP, inmutable, append-only por message_id. (hoy: data_raw/)
02_datos_limpios/ (silver) Un .db por dominio, datos normalizados. valorizaciones.db snake_case, SIN tildes, con versionado. facturacion.db pedidos_hes.db pedidos_sap.db ...
03_analitica_negocio/ (gold) Dos niveles distintos: modelo_dimensional.db dim_/fact_ — la FUENTE que Power BI importa. control_gestion.db VISTAS/métricas materializadas en SQL (trazabilidad, brechas, KPIs). Capa semántica: la consumen el análisis agéntico, el portal (sin BI) y es el staging previo a promover a PBI.
09_operacion/ (meta) Logs, runs, versionado, auditoría — FUERA de los datos de negocio. bitacora_pipeline.db (hoy disperso en ingeldata.db)Por qué dos niveles en 03_analitica_negocio
Sección titulada «Por qué dos niveles en 03_analitica_negocio»Power BI esconde lógica dentro del modelo (relaciones, medidas DAX) que un agente no puede leer ni ejecutar. Para que el análisis agéntico y el portal respondan lo mismo que PBI, esa lógica debe existir materializada en SQL:
modelo_dimensional: lo técnico (estrella dim/fact). PBI lo importa y construye encima.control_gestion: las preguntas de negocio ya respondidas como vistas SQL. Es “la capa de las ideas” — la materia prima de los expertos agénticos, del portal, y el banco de pruebas antes de promover a PBI.
- Nombres canónicos:
snake_case, sin tildes ni caracteres especiales, en toda la capa de datos. Elimina los parches de PostgreSQL/ODBC. - Definición única de métricas (DRY): cada métrica se define una sola vez, por defecto como vista SQL en
control_gestion. Power BI la importa en vez de re-calcularla en DAX → el agente y PBI leen el mismo número. Solo las métricas genuinamente interactivas (dependen de filtros del usuario en PBI) viven en DAX. - Flujo de promoción bidireccional:
- El agente explora/construye en
control_gestion→ si una métrica resulta valiosa, se promueve (PBI la importa, o el portal la expone como JSON). - Una métrica nacida en PBI (DAX) puede materializarse como vista en
control_gestionpara uso agéntico.
- El agente explora/construye en
- Una responsabilidad por capa: los logs/runs/metadata salen de los datos de negocio (
09_operacion). - Versionado consistente donde aplique (snapshots reproducibles).
Consecuencias
Sección titulada «Consecuencias»A favor:
- Fuente única por dato → fin de la triplicación y los conteos divergentes.
- La capa
control_gestionhabilita de un solo diseño: Power BI mejor fundamentado, análisis agéntico (expertos por área que leen doc + consultan esta capa sin leer todo), y reportes en el portal sin depender de BI. - Migración a Supabase segura (capas con propósito, sin mezclas).
Costo / riesgo:
- Es una reorganización que toca varios pipelines. Se mitiga con migración incremental — NO big-bang.
Migración incremental — piloto: valorizaciones
Sección titulada «Migración incremental — piloto: valorizaciones»Valorizaciones es el dominio ya limpio y servido (datos en Supabase). Es el piloto del modelo:
- Reubicar sus datos limpios en
02_datos_limpios/valorizaciones.db. - Materializar su capa
control_gestion(vistas de trazabilidad facturación↔valorización y brecha por zonal/proceso/mandante — ya validadas). - Probar el flujo agéntico → portal sobre esas vistas.
Si el piloto valida el modelo, se replica al resto de dominios. Power BI entra después, sobre la capa ya asentada.
Refinamiento: Bronze por cuenta (ingesta multi-cuenta)
Sección titulada «Refinamiento: Bronze por cuenta (ingesta multi-cuenta)»Implementado en commit
d959f53. Se activa en la re-corrida formal.
Decisión
Sección titulada «Decisión»La capa bronze (01_recepcion/) se particiona por cuenta/buzón: cada buzón escribe a un
subdirectorio propio del dataset (emails_valorizaciones_raw/<cuenta>/) y cada registro lleva
la columna cuenta que identifica su origen.
La unión + deduplicación por message_id ocurre en silver, donde se agrega la columna
cuentas_origen (lista de buzones que contenían cada correo, cubriendo el caso de correos en CC
entre cuentas).
Opt-in y retrocompatibilidad
Sección titulada «Opt-in y retrocompatibilidad»Multi-cuenta es opt-in: el campo cuentas en el spec lo activa. Por defecto (sin cuentas),
el pipeline sigue modo single-cuenta usando el campo usuario → sin breaking changes para
pipelines existentes.
Por qué este particionado
Sección titulada «Por qué este particionado»| Motivación | Detalle |
|---|---|
| Provenance nativa | Cada correo sabe de qué buzón proviene; silver agrega cuentas_origen para los que aparecen en más de uno. |
| Refresh independiente por cuenta | Re-correr un buzón no re-fetchea los otros — minimiza tráfico IMAP y tiempo de re-ingesta. |
| Resiliencia al throttling de Gmail | El conector envuelve cada cuenta en try/except; un fallo de una cuenta no bloquea las demás. |
| Paralelismo | Las cuentas son unidades de trabajo independientes en bronze; se pueden paralelizar sin coordinación. |
| Validación empírica | El Analisis-Cobertura-Buzones-2026 confirmó que HCEA + INGELSUR cubre el 67,9 % del monto trazable — justifica incorporar INGELSUR como segunda cuenta real. |
Implementación
Sección titulada «Implementación»| Artefacto | Rol |
|---|---|
CuentaIMAPConnector | Wrapper sobre el conector base; estampa cuenta en cada registro y enruta al subdirectorio correspondiente en bronze. |
domains/valorizaciones/multi_cuenta_ingest.py | Orquesta la ingesta multi-cuenta: itera cuentas, invoca CuentaIMAPConnector, acumula resultados. |
core/raw_loaders.py | Añade soporte a cuentas_origen en la carga de silver; downstream (clasificación, gold) sin cambios. |
Principios Aplicados (esta sección)
Sección titulada «Principios Aplicados (esta sección)»| Principio | Justificación |
|---|---|
| Criterio Modularizacion - Volatilidad | La cuenta (buzón) es el elemento volátil — se aísla en bronze para que agregar/quitar cuentas no toque silver ni gold. |
| Separation-of-Concerns | Bronze por cuenta (raw, particionado) / silver unifica y deduplica / gold no sabe de buzones. |
| Criterio Modularizacion - Config vs Logica | Las cuentas se declaran en el spec (configuración); la lógica de ingesta no las hardcodea. |
| Criterio Modularizacion - Testabilidad | CuentaIMAPConnector y multi_cuenta_ingest.py son unidades testeables independientemente del pipeline completo. |
Visión habilitada
Sección titulada «Visión habilitada»Esta arquitectura es la materia prima del Equipo de Expertos de Negocio (orquestador + expertos por área que consultan control_gestion con context routing: doc destilada → vistas → detalle) y del reporte agéntico en el portal de IngelCoding sin depender de Power BI.
Principios Aplicados
Sección titulada «Principios Aplicados»| Principio | Justificación |
|---|---|
| Separation-of-Concerns | Cada capa una responsabilidad: recepción / limpieza / analítica / operación. |
| Single-Source-of-Truth | Métrica definida una vez (vista en control_gestion); PBI la consume. |
| Criterio Modularizacion - DRY | Sin duplicar la lógica de negocio entre SQL y DAX. |
| Criterio Modularizacion - Config vs Logica | Datos (modelo_dimensional) separados de la semántica de negocio (control_gestion). |
| Criterio Modularizacion - Volatilidad | Capas por volatilidad: recepción inmutable vs negocio volátil. |