Bitácora de Código

El poder de tener una fuente de verdad

8 SEP 2026

ArquitecturaTypeScriptFrameworksDDDOpen Source

Este experimento nació de una pregunta incómoda: si modelos, APIs, permisos e interfaces describen el mismo negocio, ¿por qué ninguna de esas piezas conoce el sistema completo?

El poder de tener una fuente de verdad

Hace poco intenté agendar un estudio de laboratorio desde una página web.

El proceso parecía sencillo: elegir el estudio, seleccionar una sucursal, pagar y recibir la confirmación. En cambio, el sitio calculó subtotal = NaN, la API rechazó la solicitud y terminé en WhatsApp.

Ahí comenzó otro sistema.

Primero respondió un bot con botones. Después intervino una persona. Para saber qué estudios estaban disponibles tuvo que consultar información en Excel y enviarme capturas. La cita se buscó manualmente, el pago ocurrió mediante Openpay y la confirmación llegó más de una hora después.

Lo curioso es que casi ninguna parte del proceso era realmente excepcional.

Había que identificar estudios, consultar disponibilidad, calcular un precio, reservar un horario, cobrar y confirmar. Era un flujo bastante determinista. Sin embargo, el conocimiento necesario para completarlo estaba repartido entre la página, una API, un bot, hojas de cálculo, una pasarela de pago y la memoria de quien atendía WhatsApp.

El problema visible era NaN.

El problema interesante era que, cuando una pieza falló, ninguna de las demás sabía cómo continuar el proceso completo.

Un proceso determinista, repartido entre herramientas

Falló la página. Con ella también se desarmó el proceso.

Flujo esperado

  1. 01Estudio
  2. 02Sucursal
  3. 03NaN
  4. 04Horario
  5. 05Pago
  6. 06Confirmación

El sistema que apareció después de NaN

WhatsAppBot de botonesAgente humanoCapturas de ExcelOpenpayConfirmación manual

El trabajo seguía siendo mayormente determinista. Lo que desapareció fue una representación compartida del proceso.

Muchas representaciones, ninguna verdad

Esa experiencia me recordó algo que llevo encontrando durante años al construir software empresarial.

Definimos una entidad en la base de datos. Después creamos un esquema para validarla, un DTO para transportarla y una ruta para modificarla. Volvemos a describirla en OpenAPI, generamos tipos para el cliente, implementamos permisos y construimos un formulario. Más tarde aparecen auditoría, integraciones, jobs y procesos que necesitan interpretar parte de lo anterior.

Cada pieza puede estar bien diseñada.

Juntas, suelen contener versiones ligeramente distintas del mismo negocio.

Una tabla conoce las columnas de una orden. El backend sabe cómo actualizarla. El frontend conoce los campos que debe mostrar. El sistema de autorización decide quién puede tocarla. Un workflow sabe qué hacer cuando se confirma.

Pero ¿dónde vive realmente el concepto de orden?

Normalmente, en ninguna de esas piezas. Vive implícitamente entre todas ellas.

Por eso cambiar una regla puede exigir tocar el modelo, las validaciones, los endpoints, los permisos, los tipos y la interfaz. No porque el negocio tenga seis reglas diferentes, sino porque expresamos una sola regla seis veces usando abstracciones distintas.

Compartir tipos ayuda. Generar clientes ayuda. Tener un monorepo también ayuda. Pero ninguna de esas decisiones convierte por sí sola el conocimiento distribuido en una fuente de verdad.

Los tipos pueden asegurar que todos llamemos status al mismo campo. No pueden explicar por qué una orden puede confirmarse, quién está autorizado para hacerlo, qué condiciones deben cumplirse ni qué significa que haya ocurrido.

La estructura viaja. La intención se sigue perdiendo.

La misma orden, dos arquitecturas

¿Dónde vive realmente la regla del negocio?

Base de datos

status + paid_at

Validación

comprobar pago

API

POST /confirm

Permisos

alcance regional

Interfaz

botón Confirmar

Auditoría

cambió el estado

Una regla se reconstruye a partir de seis descripciones técnicas.

La fuente de verdad está antes de la infraestructura

Durante los últimos meses he estado trabajando en un framework que nace de una pregunta:

¿Qué pasaría si la infraestructura no definiera el sistema, sino que fuera una proyección del dominio?

No significa eliminar la base de datos, esconder PostgreSQL detrás de una abstracción infinita ni convertir cada aplicación en una ceremonia de Domain-Driven Design.

Significa separar los conceptos empresariales de las tecnologías que los materializan.

Una orden no es una tabla.

Confirmar una orden no es un endpoint POST.

Poder aprobar órdenes de una región no es un middleware.

Son conceptos del dominio. La tabla, el endpoint y el middleware son implementaciones posibles de esos conceptos.

El framework intenta hacer explícita esa capa anterior. El dominio declara qué existe, qué puede ocurrir, qué necesitamos consultar, qué ocurrió y bajo qué reglas puede ejecutarse una operación. A partir de esa semántica, diferentes partes del sistema pueden producir las representaciones que necesitan.

La persistencia necesita tablas. Una API necesita contratos. La interfaz necesita campos y acciones. La autorización necesita recursos y capacidades. La auditoría necesita saber qué operación ocurrió y en qué contexto.

No son fuentes de verdad independientes. Son distintas vistas de una misma realidad operativa.

Modelar acciones, no pantallas

Este cambio parece pequeño hasta que dejamos de diseñar el negocio alrededor de CRUD.

Las empresas no hablan de update order.

Una orden se confirma. Una factura se cancela. Una evidencia se aprueba. Un activo se retira.

Cada verbo conserva intención. Puede exigir permisos, validar invariantes, producir eventos o iniciar un proceso que continuará después. Cuando todo termina reducido a update(), esa información queda escondida en handlers, servicios y convenciones que el resto del sistema debe adivinar.

Por eso esta propuesta no parte solamente de modelos. También necesita acciones explícitas.

Una declaración podría comunicar que existe un activo, que conserva un número de serie, un estado y una ubicación:

const Asset = defineModel({
  id: "inventory.asset",
  fields: {
    serial: field.string({ unique: true, searchable: true }),
    status: field.selection({
      values: ["active", "maintenance", "retired"],
    }),
    location: field.relation("inventory.location"),
  },
  capabilities: {
    auditable: true,
  },
})

Pero esa declaración no debería ser una tabla disfrazada.

Su valor está en conservar suficiente significado para que una tabla, un contrato de API, metadata de auditoría o una interfaz base puedan derivarse sin convertirse en el origen del concepto.

Lo mismo ocurre con una acción como inventory.asset.retire. El nombre importa, pero no basta. El sistema también debería conocer qué recibe, quién puede ejecutarla, qué condiciones protege y qué consecuencia observable produce.

La meta no es escribir menos código a cualquier costo. Es evitar que el significado del sistema dependa de reconstruir varios fragmentos de código que nunca fueron diseñados para explicarse entre sí.

Antes del código también hay decisiones

La idea de una fuente de verdad no apareció únicamente dentro del framework.

También cambió la forma en que estamos diseñando el proyecto que lo motivó. Antes de discutir tablas o pantallas comenzamos por algo que normalmente queda relegado a una presentación: misión, visión y North Star.

Las llamamos Idealities.

No porque un documento de misión pueda generar una aplicación, sino porque las decisiones técnicas necesitan una dirección que no nazca de la tecnología disponible esa semana.

Desde ahí, el conocimiento avanza por capas:

  1. las Idealities explican qué resultado perseguimos;
  2. los documentos de diseño convierten esa dirección en decisiones de producto y sistema;
  3. los ADR conservan las elecciones arquitectónicas y sus razones;
  4. el dominio expresa los conceptos y operaciones que el software debe entender;
  5. las representaciones técnicas materializan esa intención.

Una cadena de autoridad

La fuente de verdad no es un archivo mágico.

  1. 01Idealities¿Por qué debe existir este sistema?
  2. 02Documentos de diseño¿Qué resultado y comportamiento necesitamos?
  3. 03ADR¿Qué decisiones arquitectónicas lo sostienen y por qué?
  4. 04Dominio¿Qué existe, qué puede ocurrir y bajo qué reglas?
  5. 05Proyecciones¿Cómo lo materializan DB, API, permisos e interfaz?

Cada capa responde una pregunta distinta y deriva su autoridad de la capa anterior.

El código sigue siendo indispensable, pero deja de ser el primer lugar donde intentamos descubrir qué estábamos construyendo.

Una fuente de verdad no es necesariamente un archivo único. Es una cadena explícita de autoridad: cada decisión tiene un lugar, una razón y representaciones que dependen de ella.

La parte peligrosa de esta idea

Hablar de una sola fuente de verdad puede sonar como el inicio de una abstracción que quiere controlar todo.

Esa posibilidad me preocupa tanto como me interesa la idea original.

La historia del software está llena de herramientas que prometieron independizarnos de la infraestructura y terminaron creando una infraestructura más difícil de reemplazar. Una capa semántica también puede convertirse en un nuevo punto de acoplamiento, esconder capacidades importantes o reducir tecnologías distintas al mínimo común denominador.

El framework no debería impedir una consulta especializada de PostgreSQL, una interfaz completamente personalizada o comportamiento escrito a mano cuando el dominio lo necesite.

La abstracción debe terminar donde termina su conocimiento.

Los casos repetitivos pueden derivarse. Las capacidades conocidas pueden componerse. Lo verdaderamente específico debe seguir admitiendo código específico.

Por ahora, el proyecto es precisamente eso: un experimento para descubrir dónde está esa frontera.

Por qué debe ser abierto

Hay otra condición que para mí no es negociable: el core del framework debe ser open source.

Si la propuesta consiste en que el dominio permanezca estable mientras la infraestructura puede cambiar, encerrar su definición en una plataforma propietaria contradiría el objetivo completo.

No tendría mucho sentido escapar del acoplamiento con un ORM para terminar acoplado al proveedor que define tu negocio.

Que el core sea open source no define cómo se distribuirá todo lo construido alrededor. Esa discusión pertenece a otro nivel. La decisión importante aquí es que la semántica central con la que una organización describe su dominio permanezca abierta, inspeccionable y extensible.

Todavía hay muchas preguntas abiertas.

¿Cuánta semántica puede compartirse sin crear un lenguaje gigantesco? ¿Qué debe validar el framework y qué debe dejar al runtime? ¿Cuándo una proyección deja de ser útil y necesita una implementación completamente especializada? ¿Cómo evoluciona un dominio sin romper todo aquello que fue derivado de él?

Esas preguntas son parte de por qué quiero escribir esta serie mientras construyo el framework, no cuando finja que ya está terminado.

La idea inicial, al menos, es bastante concreta.

Quiero que una empresa pueda describir cómo consigue resultados sin confundir esa descripción con las pantallas, tablas y servicios que utiliza hoy.

Quiero que una regla exista una vez como intención y que sus representaciones puedan demostrar de dónde provienen.

Quiero que, cuando una pieza falle como falló aquella página de laboratorio, el proceso no desaparezca con ella.

El sistema debería conocer el negocio que está intentando operar.

No obligarnos a reconstruirlo cada vez desde un NaN, una captura de Excel y una conversación de WhatsApp.