Proyectos

Sector: Financiero

Producción

Lakehouse financiero on-prem

Un lakehouse DuckLake self-hosted para una cooperativa financiera regulada.

Diagrama de arquitectura

El core bancario de la cooperativa —un ERP financiero propietario sobre Oracle, con más de dos décadas de operación— tenía toda la analítica atrapada dentro de la base transaccional. La vista que el propio core usa para emitir el certificado de deuda de cada pagaré tenía un bug de diseño con severidad crítica: uno de sus cuatro JOIN filtraba un concepto de cobro que no existe en el catálogo real de la cooperativa, así que esa vista devolvía cero filas de forma incondicional — cualquier certificado emitido por ese camino estaba basado en datos incorrectos o era directamente inválido. En el levantamiento inicial, la cooperativa describió el rodeo que hacían alrededor de ese bug: un equipo de 15 personas, hasta 3 días por certificado.

Diseñé un lakehouse on-prem completo, 100% software libre autoalojado, que libera la analítica del core sin tocarlo en caliente y corrige la causa raíz del bug del certificado con evidencia sobre el catálogo completo, no sobre una muestra. El proyecto avanza por módulos aceptados uno a uno. El primero está cerrado; el segundo, entregado. El tercero es la emisión automática del certificado: un único punto de aprobación humana, snapshot legal inmutable por UUID. Su caso normal (~94% de los certificados) está construido y validado de extremo a extremo.

Métricas clave

Tablas en el esquema del core, documentadas por primera vez en la historia de la cooperativa
~740Tablas en el esquema del core, documentadas por primera vez en la historia de la cooperativa
Filas en la capa analítica (20,3M en el hecho núcleo de cuotas)
~40MFilas en la capa analítica (20,3M en el hecho núcleo de cuotas)
Software libre autoalojado — sin licencia de motor propietario
100%Software libre autoalojado — sin licencia de motor propietario
Rodeo manual por certificado que la cooperativa relató en el levantamiento — reemplazado por 1 punto de aprobación humana en el caso normal
15 personas · 3 díasRodeo manual por certificado que la cooperativa relató en el levantamiento — reemplazado por 1 punto de aprobación humana en el caso normal

Stack

  • DuckLake
  • DuckDB
  • PostgreSQL
  • SeaweedFS
  • Parquet
  • Windmill
  • n8n

Decisiones y trade-offs

  1. Motor abierto sobre motor propietario

    Elegí un catálogo abierto sobre Parquet, con capacidad de motor columnar embebido, en vez de un data warehouse propietario. Portable a cualquier motor SQL estándar, sin dependencia de licencia. El costo: es tecnología de 2025, sin el historial de producción de un motor establecido — la validación exhaustiva contra el core original no fue opcional, fue la forma de compensar esa falta de historial.

  2. El bug del certificado, diagnosticado hasta la línea exacta del JOIN

    La vista QRY_PAGARE del core encadena cuatro INNER JOIN: capital, interés corriente, interés en mora y aportes. El último filtra por descripcion='APORTES' — un concepto que no existe en cartera, solo en el dominio de captaciones. Como es un INNER JOIN, cero coincidencias ahí colapsan el resultado completo: la vista devuelve 0 filas siempre, no de forma intermitente. Cualquier certificado emitido por ese camino queda basado en datos incorrectos o es directamente inválido — así lo declara el propio documento de arquitectura, con severidad crítica. Alrededor de ese bug, la cooperativa contaba desde el levantamiento inicial que emitir un certificado tomaba a un equipo de 15 personas hasta 3 días — cifra que el cliente relató, no que el proyecto midió, pero consistente con el tamaño real de esa oficina (25 personas). La corrección no fue un parche sobre el core —eso habría requerido tocar producción en caliente—: vive en el modelo Gold nuevo (gold.datos_certificado), que elimina el componente APORTES del JOIN (el aporte social pertenece a captaciones, no a cartera) y reemplaza los cuatro INNER JOIN por un único SUM por concepto sobre un solo recorrido de la tabla de hechos. Se validó contra los 40 conceptos reales del catálogo (cat_con_descuento.psv), no contra una muestra — ninguno de los 40 es un aporte social ordinario de cartera.

  3. Una segunda validación, ya en Módulo C: el saldo que se queda atrás

    Al construir el motor de emisión de Módulo C se encontró un segundo problema, de otra naturaleza: el campo administrativo VLR_SALDO_ACTUAL del core puede ir desactualizado frente al saldo real. El motor de cálculo lo usó primero por ser el campo obvio; un job de validación cruzando cada resultado contra el core original —al centavo— lo detectó antes de pasar a producción, el mismo día que se construyó. La corrección: calcular el saldo analítico sumando directamente los montos pendientes de la tabla de hechos, filtrados por capital, estado activo y no vendido, en vez de leer el campo administrativo. Es la misma disciplina que exige la primera decisión (nada llega a producción sin haberse reproducido exacto contra el core), aplicada dos veces en el mismo proyecto, sobre dos bugs distintos.

  4. Clasificar por código, nunca por texto

    El core filtra conceptos financieros por descripción de texto (DESCRIPCION = 'CAPITAL') — frágil: si el proveedor del core renombra un concepto, el filtro se rompe en silencio, sin error visible. El modelo nuevo clasifica por código numérico estable. Texto frágil en vez de identificador estable: la causa raíz exacta del bug del certificado, corregida en el diseño para que no vuelva a pasar en ningún otro reporte.

  5. El módulo que queda vacío, y se declara así, no se esconde

    El módulo de captaciones (ahorros, aportes, CDAT) está completamente vacío en la fuente que se usó para diseñar y validar. No es un error del modelo: es un dato de negocio que no llegó. El diseño prepara el contrato de datos para esa tabla pero no la puebla — y mientras tanto, los saldos contables de aporte y ahorro por asociado sí están disponibles, identificados por la clase contable del plan de cuentas. La consecuencia negativa, dicha sin adorno: hasta que exista la fuente transaccional de captaciones, esa vista da una foto parcial, no completa, del asociado.