Parte V · En la práctica
El dominio más exigente: un banco
En casi todos los dominios, las decisiones de diseño son cuestión de buen gusto. En un banco son cuestión de ley. Aquí veremos si el modelo soporta el peso del dinero sin doblarse.
Son las 02:13 de un martes. En un servidor de un banco regional, un proceso de cierre acaba de detenerse: dos cifras que llevaban diez años coincidiendo dejaron de coincidir por catorce centavos. No es un error de cálculo; es un error de historia. Alguien, en algún sistema satélite, sobrescribió un dato que un auditor necesitaba leer tal como estaba la semana pasada, y ahora ese pasado ya no existe. Multiplica esos catorce centavos por millones de operaciones y por la firma de un regulador que exige reconstruir cualquier instante, y tendrás el motivo por el cual el dominio bancario es la prueba de fuego de cualquier modelo de datos. No perdona la elegancia que no resiste una citación judicial.
Conviene fijar la escala. Una empresa mediana cualquiera guarda su operación en unas ciento cincuenta tablas, un par de sistemas heredados y un programa de contabilidad. Un banco regional de tamaño medio juega en otra liga: alrededor de mil quinientas tablas solo para el núcleo, otro millar para tarjetas de crédito, otro tanto para seguros, y una nube de aplicaciones satélite para microcréditos, prevención de fraude y la aplicación móvil. Cada sistema tiene su propio dueño, sus propias reglas y, lo más corrosivo, su propio idioma.
En el capítulo del spa demostramos que el modelo era ágil; en el del taxi, que aguantaba la velocidad; en la historia clínica, que sabía repartir la verdad entre varios custodios. El banco reúne las tres exigencias y añade una cuarta que las domina a todas: el regulador. Aquí las decisiones de diseño que en otros capítulos eran preferencias (no borrar nunca el pasado, tratar al software como un agente con responsabilidad) dejan de ser lujos teóricos y se vuelven condiciones de supervivencia dictadas por la ley.
Este capítulo no aspira a ser un manual de core banking. Aspira a algo más modesto y más contundente: tomar cuatro escenas (una transferencia, la vida entera de un préstamo, una investigación de fraude y el catálogo de productos) y demostrar que el modelo absorbe la complejidad industrial sin trucos. Si aguanta el dinero, aguanta cualquier cosa.
La realidad honesta: cinco islas que no se hablan
Antes de presumir de soluciones, describamos el enfermo. Quien trabaja en banca asentirá; quien no, se asombrará. Por dentro, un banco no es un sistema: es un archipiélago de cinco islas que apenas se saludan.
- Isla 1 · El núcleo. El servidor central, el core bancario. Allí viven los saldos, los movimientos oficiales y la contabilidad pesada. Es la «verdad» que el Estado audita.
- Isla 2 · Las agencias. Cada sucursal corre pequeños programas para simular créditos o repartir turnos. Toman decisiones todos los días, pero rara vez le explican al núcleo cómo llegaron a ellas.
- Isla 3 · Contabilidad. El zoológico oculto. Una parte vive en el servidor; otra, enorme, vive en hojas de cálculo que se ajustan a mano a fin de mes. Cuando el núcleo y la contabilidad discrepan, suele ganar contabilidad —y casi nadie sabe explicar por qué.
- Isla 4 · Los promotores. Los agentes de calle que venden créditos anotan datos en planillas y papeles. Esa información valiosa del cliente es «fantasma» hasta el día en que el crédito se aprueba y recién entonces ingresa al banco.
- Isla 5 · La reportería. Cuando la gerencia pide un reporte que cruce las cuatro islas anteriores, el equipo técnico tarda semanas en cavar un túnel temporal que junte todo en un data lake. A veces los números no cuadran y el proyecto naufraga.
¿Por qué ocurre? Porque cuando un banco intenta modernizarse, comete el mismo error: obligar a todas las islas a hablar un único idioma robótico. Y reprogramar un sistema de quince años para que hable «moderno» sale tan caro que los bancos prefieren dejarlo morir en paz. El resultado es un empate técnico permanente: nadie migra, nadie se entiende.
El giro de WQuestions
El modelo no le exige a la sucursal ni a contabilidad que cambien su software. Solo les pide que traduzcan lo que ya hacen y lo publiquen al mapa central (el grafo) usando su propia jerga. Contabilidad habla de «partidas»; la agencia habla de «simulaciones». El lexicon traduce ambos dialectos al catálogo universal una sola vez, y a partir de allí cada isla sigue operando como siempre mientras la gerencia consulta el mapa en milisegundos.
El banco sobre las siete coordenadas
Para domar el caos, repartamos las piezas del banco en los ejes de valor. El ejercicio es revelador por sí solo: lo que parecía una maraña de mil quinientas tablas se ordena en seis preguntas.
Q quién · agentes
Clientes físicos, empresas (personas jurídicas) y
—decisivo— los sistemas. El motor antifraude y el autorizador de la
red de tarjetas deciden solos; por lo tanto, viven en Q como agentes
activos con responsabilidad.
O qué · entidades y eventos
Cuentas, préstamos, movimientos de dinero, tarjetas, asientos contables e investigaciones de fraude. Aquí se concentra el noventa por ciento del peso del banco.
L dónde · lugares
Sucursales físicas, cajeros automáticos y lugares virtuales como la aplicación móvil o la banca web. El canal importa: una operación por cajero no se gobierna igual que una por app.
T cuándo · tiempos
Cuidado aquí: hay triple reloj. La hora en que el cliente ordenó el pago, la hora en que el servidor lo procesó y la fecha del cierre contable. El modelo anota los tres sin confundirlos.
N cuánto · magnitudes
Dinero, tasas de interés, plazos en meses y puntajes de riesgo. Cada magnitud arrastra su unidad: ningún número anda suelto.
K cuál · clases
Tipos de cuenta, monedas (dólar, euro, sol), estados de mora y códigos legales. El zócalo categórico que da sentido a todo lo demás.
Los predicados del eje cómo (el séptimo eje, M)
son los cables que conectan todo: agente, parte_de,
cubierto_por, cancela. Sin ellos las seis cajas serían seis
listas inertes. Con ellos, un hecho bancario se vuelve un punto navegable. Pasemos a verlo
funcionar.
Caso 1 · Una transferencia y sus cinco agentes ocultos
Para un humano, una transferencia es una frase de seis palabras: «Mariana le envía 480 dólares a Bruno». Para el sistema de un banco, ese gesto moviliza a cinco agentes y deja dos registros espejo en contabilidad. Veamos cómo lo despliega el modelo.
transferencia_7791
como evento central. Cuatro agentes la rodean (quien inicia, quien recibe, el sistema que
autoriza y el motor que verifica) y dos asientos contables cuelgan de ella por
parte_de; el departamento de contabilidad es el quinto agente, implícito en
las partidas. Nada de esto cabe en una sola fila de una tabla.Primero declaramos el evento central. Observa que tres de sus enlaces apuntan a agentes y que uno de ellos (el motor antifraude) no es una persona:
(transferencia_7791) ∈ O
instancia_de: accion_transferir
agente: mariana_q # Agente 1 — inicia
beneficiario: bruno_q # Agente 2 — recibe
cuenta_origen: cta_mariana_4410
cuenta_destino: cta_bruno_8820
monto: n_480_usd
lugar_de: app_movil_banco
autorizado_por: banca_web_sys # Agente 3 — da el visto bueno
verificado_por: antifraude_sys # Agente 4 — descarta el robo
ts_orden: 2026-05-19T21:07:44 # reloj del cliente
ts_proceso: 2026-05-19T21:07:46 # reloj del servidor
Pero la transferencia no termina ahí. Tras bambalinas, contabilidad (el quinto agente)
debe cuadrar sus libros. La partida doble es una ley de la profesión: a todo débito le
corresponde un crédito. En el modelo creamos dos subeventos y los colgamos
de la transferencia con el cable parte_de:
(asiento_debito_7791) ∈ O # le retiramos saldo a Mariana
parte_de: transferencia_7791
cuenta_contable: ahorros_mariana_interna
monto: n_480_usd
tipo_movimiento: debito
(asiento_credito_7791) ∈ O # le abonamos saldo a Bruno
parte_de: transferencia_7791
cuenta_contable: ahorros_bruno_interna
monto: n_480_usd
tipo_movimiento: credito
La misma escena, vista como tres hechos atómicos, queda así de explícita: el evento y sus dos contrapartidas, unidos por un solo predicado.
D5 El software es un agente
El motor antifraude no es un campo ni una bandera bool:
es un agente del eje Q con responsabilidad sobre el hecho. Cuando
antifraude_sys deja pasar una operación, esa decisión queda firmada con su
nombre, igual que la firma de un cajero humano. Si mañana hay que rendir cuentas de por
qué pasó, el responsable está en el grafo, no perdido en un log que nadie guardó.
Y si esta operación resultara mal hecha, jamás la borramos. Creamos una transferencia rectificativa que apunta a la vieja y la anula. La ley obliga a los bancos a conservar el historial completo de los errores, no solo el resultado pulido; el modelo lo hace por defecto, sin tablas de auditoría aparte. Volveremos sobre esto en el Caso 3.
Queda una última arruga de la transferencia, y no se deshace con agentes ni con asientos, sino con el reloj. La de Mariana a Bruno se liquidó en dos segundos porque era interna: el mismo banco movía dinero entre dos de sus cuentas. Invierte ahora los papeles. A Mariana le envían un giro desde otro banco y, peor aún, desde el extranjero, un viernes por la tarde.
El dinero ya salió de la cuenta del remitente, pero a la de Mariana todavía no entra: queda suspendido en una cámara de compensación, esperando a que los sistemas abran. Y como el viernes la ventana ya cerró, tendrá que esperar hasta el lunes para disponer de su propio dinero. En pleno siglo XXI, un par de días de limbo para un giro que la pantalla ya da por enviado, y el dinero podía ser urgente.
Que esa espera exista no es culpa de ningún modelo de datos: responde a ventanas de compensación, husos horarios, fines de semana y reglas regulatorias que ningún grafo borra. Pero sí es culpa del modelo plano que Mariana no pueda saber dónde está su dinero mientras tanto; un sistema clásico le muestra un opaco «pendiente» y poco más. WQuestions no trata la transferencia como un instante, sino como un evento con estados fechados (la misma vigencia temporal que el siguiente caso convertirá en regla):
(giro_int_8043, estado, ordenada, inicio=2026-05-15T17:40, fin=2026-05-15T17:41)
(giro_int_8043, estado, en_compensacion, inicio=2026-05-15T17:41, fin=2026-05-18T09:02)
(giro_int_8043, estado, disponible, inicio=2026-05-18T09:02, fin=hoy)
(giro_int_8043, fecha_valor, 2026-05-18) # cuándo el dinero es de Mariana
(giro_int_8043, ts_orden, 2026-05-15T17:40) # viernes: cuándo se ordenó
Modelada así, la pregunta angustiada del cliente («¿dónde está mi dinero y cuándo lo tendré?») deja de ser un secreto que solo el banco conoce: es una consulta que el grafo contesta al instante, con el estado vigente y la fecha en que el dinero se libera. El modelo no acelera el giro; los dos días de espera siguen ahí. Lo que hace es convertir ese limbo opaco en un hecho explícito, fechado y consultable, que es justo lo que el cliente (y el regulador) tienen derecho a ver.
Caso 2 · La novela de un préstamo y la regla del tiempo
Un préstamo no es un suceso de un segundo: es una novela que dura años. Se aprueba, se pagan cuotas, el cliente se atrasa (entra en mora), a veces la deuda se reestructura y, con suerte, todo termina cancelado. En un sistema heredado, la celda «estado» del préstamo se va borrando y reescribiendo, y con cada reescritura desaparece un capítulo de la historia.
Aquí aplicamos la regla de vigencia temporal. Cada vez que el préstamo de Mariana cambia de estado, no borramos nada: inyectamos una línea nueva con su fecha de inicio y su fecha de fin. El pasado se acumula, no se pierde.
D6 Vigencia temporal: el pasado no se sobrescribe
Ningún hecho que pueda cambiar se guarda como un valor desnudo; se
guarda con un intervalo [inicio, fin). Cambiar de estado no es modificar una
celda: es cerrar el intervalo vigente y abrir uno nuevo. La consecuencia
es que el grafo conserva, gratis, toda la trayectoria de cualquier atributo a lo largo del
tiempo.
(prestamo_5503, estado, vigente, inicio=2026-01-20, fin=2026-08-15)
(prestamo_5503, estado, mora_30_dias, inicio=2026-08-15, fin=2026-09-15)
(prestamo_5503, estado, mora_60_dias, inicio=2026-09-15, fin=2026-10-22)
(prestamo_5503, estado, reestructurado, inicio=2026-10-22, fin=hoy)
Cinco años después, en plena audiencia, el juez pregunta: «¿En qué estado exacto se encontraba este préstamo el 1 de septiembre de 2026?». La base filtra por fecha y responde sin titubear: «Mora de treinta días». Sin la regla D6, los bancos tienen que construir costosas tablas paralelas solo para almacenar estos fantasmas del pasado —y rezar para que alguien las haya mantenido al día.
motivado_por; el rastro entero se preserva. La
reestructuración puede devolver el préstamo a estado vigente sin borrar la mora que
ocurrió: el pasado sigue ahí, fechado.Hay un detalle fino que la figura insinúa. Una transición no es solo un cambio de
etiqueta: es un hecho que merece su propia causa. Por eso cada cambio de estado lleva un
enlace motivado_por que apunta al evento que lo provocó (un pago vencido, un
acuerdo firmado) y, cuando hace falta, un justificado_por que apunta al
documento legal. El «por qué» no es un eje, como vimos en su momento; es una relación entre
hechos, y aquí esa relación es la que un auditor leerá primero.
Caso 3 · Investigación de fraude: reconstruir la noche del cargo
Mariana llama indignada: alguien usó su tarjeta para pagar 1.840 dólares en una tienda de electrónica al otro lado del país, de madrugada, mientras ella dormía. El banco debe abrir una investigación. Y es justo aquí donde la arquitectura se separa de la competencia por una distancia que no se cierra con más servidores.
El investigador no necesita el saldo de Mariana de hoy. Necesita viajar a la noche del cargo y preguntar dos cosas que un modelo plano no sabe responder: ¿dónde creía el banco que estaba Mariana esa noche?, y ¿qué decía su perfil de riesgo a esa hora exacta? La respuesta correcta no es el valor actual del perfil; es el valor que el perfil tenía entonces.
Gracias a la vigencia, el perfil de riesgo de Mariana no es un dato que se pisa: es una serie de versiones fechadas. El investigador recupera la versión que estaba activa la noche del fraude:
(perfil_riesgo_mariana_v4, instancia_de, perfil_antifraude,
inicio=2026-04-30, fin=2026-06-02)
(perfil_riesgo_mariana_v4, score_riesgo, 0.27)
(perfil_riesgo_mariana_v4, geo_habitual, ciudad_costa)
Con eso a la vista, el investigador entiende por qué la tarjeta pasó esa noche: el
score_riesgo era bajo y el comercio no levantó alertas suficientes. Al
confirmar el robo, no borra el pago de la tienda (el cargo fue real y
movió dinero de verdad). En su lugar crea un evento de reverso que apunta al original y deja
escrita la causa:
(reverso_cargo_3120, cancela, autorizacion_original_3120)
(reverso_cargo_3120, justificado_por, investigacion_fraude_0457)
(reverso_cargo_3120, monto, n_1840_usd)
(reverso_cargo_3120, ts_proceso, 2026-05-21T09:40:11)
El historial queda intacto: el cargo fraudulento sigue allí, fechado, y a su lado el reverso que lo neutraliza. El dinero vuelve y el banco conserva una prueba impecable para presentar al seguro. Esto es lo que un sistema plano no puede ofrecer sin reconstrucciones heroicas: la capacidad de mostrar no solo el resultado, sino la secuencia exacta de lo que se supo y cuándo.
La trampa del UPDATE
En el modelo plano, corregir un fraude tienta a ejecutar un
UPDATE perfiles_riesgo SET nivel='alto' y un DELETE del cargo
falso. Es la decisión que destruye el caso: al actualizar el perfil, se borra justamente
la prueba de que esa noche el sistema lo creía bajo; al borrar el cargo, se borra la
evidencia del robo. El reflejo más natural del programador es, aquí, el peor enemigo del
banco. El modelo lo previene haciendo que el UPDATE destructivo no sea
siquiera una operación disponible.
Caso 4 · Una tarjeta «Platino» no es una idea: es un objeto
Resolvamos, por último, un error de modelado tan común como costoso. Cuando el banco le
entrega a Mariana una «Tarjeta Platino», muchos programadores asumen que «Platino» es una
categoría abstracta y la guardan en la caja K. Es falso, y la falsedad tiene
consecuencias millonarias.
Producto como oferta reificada
La «Oferta Platino del primer trimestre de 2026» no es una etiqueta: es
un objeto real y reificado de la caja O, con un contrato
anexo, unas tasas, una cuota de manejo y una fecha de caducidad. El tipo tarjeta
platino sí vive en K; la oferta concreta que un cliente firmó,
no. Confundir la clase con la instancia es confundir la receta con el plato servido.
Cuando el banco entrega el plástico, conecta la tarjeta de Mariana a la oferta específica que estaba vigente ese día —no a la idea de «tarjeta platino», sino al contrato exacto que ella aceptó:
(oferta_platino_2026_t1) ∈ O # el contrato de oferta del banco
instancia_de: tipo_tarjeta_platino # ← su clase en K
cuota_manejo_mensual: n_9_usd
tasa_anual: n_38_pct
vigencia: inicio=2026-01-01, fin=2026-06-30
(tarjeta_mariana_2255) ∈ O # el plástico que tiene Mariana
cliente: mariana_q
cubierto_por: oferta_platino_2026_t1
¿Por qué es vital separar la oferta de la clase? Porque si en julio el banco lanza una versión nueva de la tarjeta con cuota de manejo de doce dólares, la tarjeta de Mariana no puede encarecerse por arte de magia: ella firmó el contrato de la oferta de enero, y ese contrato sigue siendo un objeto congelado en el tiempo, con su propia vigencia. Al tratar los productos financieros como objetos independientes y fechados, el banco se blinda contra demandas multimillonarias por cambiar las reglas sin avisar.
En banca, los productos no son adjetivos: son contratos firmados que el tiempo no debería poder reescribir.La lección del Caso 4
El antes y el después: del esquema fragmentado al grafo único
Pongamos las dos arquitecturas frente a frente con una sola pregunta, deliberadamente modesta: ¿cuáles fueron las dos contrapartidas contables de la transferencia 7791 y qué perfil de riesgo tenía Mariana en ese instante exacto?
Antes, en el modelo relacional. La información se reparte entre tablas
como clientes, cuentas, transferencias,
asientos y perfiles_riesgo. La primera mitad de la pregunta se
resuelve con un join de cuatro tablas (incómodo, pero posible). La segunda mitad
fracasa, porque perfiles_riesgo solo guarda el registro actual. Para
saber qué decía el perfil la noche del cargo hay que rebuscar en tablas de auditoría
separadas, si es que alguien las creó; de lo contrario, la respuesta sencillamente no existe
en los datos.
-- Antes: perfiles_riesgo solo guarda el estado ACTUAL — la historia se pierde.
CREATE TABLE transferencias (id INTEGER PRIMARY KEY, monto NUMERIC, ts TEXT);
CREATE TABLE asientos (id INTEGER PRIMARY KEY, transferencia_id INT, tipo TEXT, monto NUMERIC);
CREATE TABLE perfiles_riesgo(cliente_id INTEGER PRIMARY KEY, nivel TEXT); -- sin vigencia
-- Las dos contrapartidas contables de la transferencia: sale bien.
SELECT tipo, monto FROM asientos WHERE transferencia_id = 7791;
-- "¿Qué nivel de riesgo tenía Mariana la noche del cargo?"
-- Imposible: la tabla solo tiene el valor de hoy, no el de entonces.
SELECT nivel FROM perfiles_riesgo WHERE cliente_id = 4410;
Eso no es un defecto de un banco en particular: es el defecto estructural del modelo plano frente a la bitemporalidad. Qué ocurrió y qué sabía el sistema cuando ocurrió son dos ejes de tiempo distintos, y el esquema clásico los aplasta en una sola columna.
Después, en WQuestions. La misma información vive como un grafo de
hechos. La transferencia transferencia_7791 se liga por parte_de
a sus dos asientos; el perfil perfil_riesgo_mariana_v4 lleva su propio rango
de vigencia inicio=2026-04-30, fin=2026-06-02. La pregunta entera se vuelve un
patrón de proyección: recoge los hechos cuyos extremos cuelgan de la transferencia y suma
el perfil cuyo intervalo temporal solapa con la marca de tiempo del cargo.
No hay join de cuatro tablas, no hay tabla de auditoría paralela (el tiempo es un atributo de primer orden en cada hecho) y, sobre todo, no hay que migrar ningún esquema para que funcione. El banco no abandona sus mil quinientas tablas: las deja publicar al grafo. La consulta que antes era un proyecto de semanas pasa a ser una sola pregunta bien formada, y la respuesta que antes «no existía en los datos» pasa a estar siempre disponible, fechada al segundo.
Del préstamo de Mariana a la cartera entera
Una transferencia de Mariana a Bruno, la novela de un préstamo, un cargo fraudulento: casos
que se entienden de a uno. El regulador, en cambio, nunca pregunta por Mariana. Pregunta por
la cartera entera: cuánto saldo sigue vivo en cada estado de mora, qué proporción de los
préstamos lleva más de sesenta días vencido, cómo se movió ese número de un mes al otro. Y
esas respuestas no piden tablas nuevas. El préstamo prestamo_5503 ya guarda su
estado fechado con la vigencia D6; el reporte regulatorio es leer ese mismo estado sobre
todos los préstamos a la vez y sumar sus montos.
# Saldo vivo de los préstamos en mora de 60 días — un corte; el reporte recorre cada estado
suma(u, "monto", Pattern(fixed={"estado": u.ind("mora_60_dias")},
type_constraint=u.ind("prestamo")))
Lo que el banco le exigió al modelo
Vale la pena cuantificar de qué tamaño es el problema que el modelo absorbe. Estas son las tablas aproximadas que un banco regional dedica a cada frente (el orden de magnitud que obliga a las cinco islas a existir):
Reunamos lo que estas cuatro escenas pusieron a prueba. Ninguna pidió una extensión del modelo; todas cayeron sobre la misma maquinaria que el libro venía construyendo:
El veredicto del dominio más exigente
- Agentes no humanos con responsabilidad. Sistemas
que autorizan y verifican operaciones viven en el eje
Qy firman sus decisiones (D5). El motor antifraude rinde cuentas igual que un cajero. - Contabilidad de partida doble. Eventos que se
ramifican en una cara operativa y dos caras contables sin perder el hilo que las une, a
través de
parte_de. - Auditoría indestructible del pasado. La vigencia temporal (D6) preserva el rastro de estados, deudas y reglas obsoletas, y blinda al banco ante juicios y auditorías sin tablas paralelas.
- Productos como objetos fechados. La oferta firmada se congela en el tiempo, de modo que un cambio futuro de tarifas nunca reescribe un contrato vigente.
El banco no logró tumbar al modelo. Y lo decisivo no es que «aguantara», sino cómo lo hizo: las mismas reglas que en el spa parecían refinamientos elegantes resultaron ser, en banca, las únicas que sobreviven al regulador. Cuando una decisión de diseño que nació por gusto reaparece como exigencia legal en el dominio más duro, deja de ser una hipótesis. En el próximo capítulo subiremos un peldaño en complejidad organizativa: un ERP donde una sola venta atraviesa inventario, finanzas y logística a la vez.