Ir al contenido

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. :::

El inventario del estado actual (2026-06-13) reveló una capa de datos sin capas ni propósito claro:

  1. ingeldata.db es 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.
  2. Mismo dato en 3 lugares con conteos distintos: p. ej. facturas en ingeldata.db (325), curated/facturacion.db (3) y data_warehouse.db (2.584). No hay fuente única.
  3. Nombres inconsistentes: snake_case en unas DB, Title_Case con tildes (N°_Pedido) en otras → rompe PostgreSQL/ODBC y obliga a parches de normalización.
  4. 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.
  5. Sin sincronización garantizada entre ingeldata.db (operacional) y data_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.
  1. Nombres canónicos: snake_case, sin tildes ni caracteres especiales, en toda la capa de datos. Elimina los parches de PostgreSQL/ODBC.
  2. 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.
  3. 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_gestion para uso agéntico.
  4. Una responsabilidad por capa: los logs/runs/metadata salen de los datos de negocio (09_operacion).
  5. Versionado consistente donde aplique (snapshots reproducibles).

A favor:

  • Fuente única por dato → fin de la triplicación y los conteos divergentes.
  • La capa control_gestion habilita 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:

  1. Reubicar sus datos limpios en 02_datos_limpios/valorizaciones.db.
  2. Materializar su capa control_gestion (vistas de trazabilidad facturación↔valorización y brecha por zonal/proceso/mandante — ya validadas).
  3. 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.

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).

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 usuariosin breaking changes para pipelines existentes.

MotivaciónDetalle
Provenance nativaCada correo sabe de qué buzón proviene; silver agrega cuentas_origen para los que aparecen en más de uno.
Refresh independiente por cuentaRe-correr un buzón no re-fetchea los otros — minimiza tráfico IMAP y tiempo de re-ingesta.
Resiliencia al throttling de GmailEl conector envuelve cada cuenta en try/except; un fallo de una cuenta no bloquea las demás.
ParalelismoLas cuentas son unidades de trabajo independientes en bronze; se pueden paralelizar sin coordinación.
Validación empíricaEl Analisis-Cobertura-Buzones-2026 confirmó que HCEA + INGELSUR cubre el 67,9 % del monto trazable — justifica incorporar INGELSUR como segunda cuenta real.
ArtefactoRol
CuentaIMAPConnectorWrapper sobre el conector base; estampa cuenta en cada registro y enruta al subdirectorio correspondiente en bronze.
domains/valorizaciones/multi_cuenta_ingest.pyOrquesta la ingesta multi-cuenta: itera cuentas, invoca CuentaIMAPConnector, acumula resultados.
core/raw_loaders.pyAñade soporte a cuentas_origen en la carga de silver; downstream (clasificación, gold) sin cambios.
PrincipioJustificación
Criterio Modularizacion - VolatilidadLa 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-ConcernsBronze por cuenta (raw, particionado) / silver unifica y deduplica / gold no sabe de buzones.
Criterio Modularizacion - Config vs LogicaLas cuentas se declaran en el spec (configuración); la lógica de ingesta no las hardcodea.
Criterio Modularizacion - TestabilidadCuentaIMAPConnector y multi_cuenta_ingest.py son unidades testeables independientemente del pipeline completo.

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.


PrincipioJustificación
Separation-of-ConcernsCada capa una responsabilidad: recepción / limpieza / analítica / operación.
Single-Source-of-TruthMétrica definida una vez (vista en control_gestion); PBI la consume.
Criterio Modularizacion - DRYSin duplicar la lógica de negocio entre SQL y DAX.
Criterio Modularizacion - Config vs LogicaDatos (modelo_dimensional) separados de la semántica de negocio (control_gestion).
Criterio Modularizacion - VolatilidadCapas por volatilidad: recepción inmutable vs negocio volátil.