Saltar a contenido

Por qué un BFF y no queries directas

Cuadrante 04_EXPLANATION — el porqué, no el cómo. Si vienes a resolver una tarea, ve a 02_HOW_TO/.

La tentación

El panel muestra clientes de Gastro y de Travel. Las tres bases viven en el mismo Postgres. Un SELECT con dos conexiones y listo: menos código, menos latencia, menos piezas.

Y sin embargo Operator habla con las verticales solo por HTTP, con un token, pidiéndoles a ellas que hagan el trabajo. Vale la pena saber por qué, porque el atajo va a volver a parecer buena idea.

Razón 1: el esquema de otro no es una API

Si el panel hiciera SELECT plan_type FROM gastro_tenants, esa columna quedaría congelada. Renombrarla en Gastro —una refactorización interna, de su propio dominio— rompería un panel que vive en otra carpeta, con otro CI, que su autor quizá ni recuerda. El acoplamiento no se ve en ningún import: aparece en producción, un martes.

Con HTTP, el contrato es explícito y versionado. Gastro puede renombrar lo que quiera mientras /platform/tenants siga respondiendo lo mismo.

Razón 2: las reglas de negocio no se pueden copiar

Suspender un tenant de Gastro no es UPDATE gastro_tenants SET is_active = false. Es eso, más invalidar el caché de su carta pública, más escribir la auditoría, más lo que Gastro decida mañana que también hace falta.

Un UPDATE desde el panel produciría un tenant a medio suspender: la base dice una cosa, el caché sirve otra. Y nadie se entera hasta que un comensal escanea el QR de un restaurante que "ya no está".

Travel es todavía más claro. Su ciclo de cobro tiene dunning: un job que mira estados y fechas para decidir a quién recordarle, a quién marcar en mora y a quién expirar. Mover trial_ends_at con un UPDATE a espaldas de esa lógica es cómo se le manda un correo de cobro a un cliente al que le acabas de regalar una extensión. Por eso las mutaciones de Travel pasan por las primitivas de su dominio de billing: no para respetar una capa por gusto, sino porque esa capa sabe cosas que el panel no.

Razón 3: la auditoría tiene que ser atómica, y solo la dueña puede

La bitácora es el punto entero del panel: quién cambió qué y por qué. Si el panel escribiera el cambio en gastro_db y el audit en otra transacción, cualquier fallo entre las dos deja un cambio sin rastro — precisamente el agujero que veníamos a tapar.

La vertical dueña escribe ambas cosas en una transacción. Un cambio sin su fila de bitácora no existe.

Razón 4: el navegador nunca debe poder hacer esto

El token que abre /platform/* de Gastro vive en el servidor del BFF. El navegador solo tiene un JWT de operador, que fuera de ops.timeliber.com.co no vale nada.

Si el panel fuera un frontend hablando directo con las verticales, ese token tendría que estar en el bundle — es decir, público. No hay forma de esconder un secreto en JavaScript que se descarga.

Qué cuesta

Honestamente:

  • Latencia: cada pantalla es un salto HTTP extra. Se paga con timeout (10s) y degradación: si una vertical no responde, el panel dice que está caída en vez de colgarse con ella.
  • Duplicación aparente: CustomerCard se parece a TenantCard de Gastro. No es duplicación: es traducción. TenantCard habla el idioma de Gastro; CustomerCard, el del panel. Que hoy se parezcan es coincidencia, no una razón para fusionarlos.
  • Un dominio platform/ por vertical: código nuevo en cada una. A cambio, cada vertical decide qué expone y qué no.

La regla, en una línea

El panel orquesta y traduce. La vertical decide y ejecuta. Si te encuentras escribiendo lógica de negocio en el BFF —qué plan puede pasar a cuál, cuántos días de prueba son válidos— estás en el archivo equivocado: eso va en la vertical dueña, y el BFF solo propaga su respuesta.

Relacionado