Skip to main content

04. Arquitectura de Datos: MDX-Cloud

Esta sección especifica la estrategia de persistencia distribuida para MDX-Cloud. Para garantizar la integridad absoluta de la información ante fallas de red, operación multisucursal y alto rendimiento en bases de datos masivas, se implementa un modelo híbrido: persistencia relacional estricta en el servidor y resolución de lecturas en el cliente (Client-Side Joins).

04.1 Estrategia de Identidad Controlada (UUIDv7)

Para soportar la continuidad operativa Offline-First, se elimina por completo el uso de llaves primarias numéricas autoincrementables (SERIAL / IDENTITY) en las tablas transaccionales de la base de datos central.

  • Regla de Identidad: Todas las entidades del sistema utilizan identificadores únicos globales secuenciales basados en tiempo: UUIDv7.
  • Generación en la Fuente: Las llaves primarias se generan directamente en el dispositivo cliente (SPA en React) al momento de capturar la transacción (offline u online), o en la capa de Dominio de .NET 10.
  • Control y Rendimiento (Anti-Fragmentación): A diferencia de UUIDv4 (completamente aleatorio), UUIDv7 incrusta el timestamp (fecha y hora exacta en milisegundos) en sus primeros bits. Esto permite que el cliente "controle" la marca de tiempo de creación y garantiza que, al insertarse en PostgreSQL, los registros entren ordenados cronológicamente de forma nativa. Esto erradica la fragmentación del índice B-Tree (Page Splits) y asegura lecturas masivas a ultra-alta velocidad.

04.2 Estructura de Esquemas Lógicos y Resolución de Lecturas

Mantenemos un único motor físico de base de datos PostgreSQL en el VPS, pero segmentado en 10 esquemas lógicos independientes (Domain-Driven Design).

El Patrón de Aislamiento Parcial y Red de Seguridad (FKs)

(Última modificación: 2 de Junio de 2026)

Dado el tamaño ágil del equipo de ingeniería (2 desarrolladores) y para garantizar una facilidad extrema de depuración junto con una integridad de datos a prueba de balas, el sistema adopta un modelo pragmático híbrido:

  1. Llaves Foráneas (FKs) como Red de Seguridad (Fallback):
    • Se permite explícitamente el uso de Llaves Foráneas (FKs) físicas cruzando distintos esquemas lógicos en PostgreSQL (Ej. de ventas.pedidos a crm.clientes).
    • Regla de Negocio: El código C# (.NET) no debe usar los errores de base de datos (DbUpdateException) como su primera línea de lógica de negocio. C# debe validar las existencias y reglas usando .Contracts en memoria para devolver mensajes de error limpios. Las FKs físicas en PostgreSQL actúan única y exclusivamente como una Red de Seguridad (Safety Net) por si un bug se escapa de la capa de aplicación, asegurando que la base de datos sea el candado final.
  2. Operaciones Inter-Dominio (Transacciones Síncronas en C#): Para procesos que afectan a múltiples módulos (ej. Registrar una venta y descontar inventario), NO se utilizan Sagas ni Consistencia Eventual. Todo ocurre en una única transacción síncrona controlada por Entity Framework Core (IDbContextTransaction).
    • Ejecución Lineal: Se abre una transacción de base de datos en .NET. Se guarda la factura y se descuenta el stock simultáneamente.
    • Rollback Automático: Si algo falla en cualquier punto del flujo (ej. la base de datos escupe un error de Llave Foránea o falta de stock), la transacción entera sufre un Rollback instantáneo y el middleware de .NET captura la excepción para arrojar un Log enriquecido en Grafana. Nada se guarda parcial o corruptamente en PostgreSQL.
  • Lecturas (Queries) y "Client-Side Joins": Para no saturar la RAM del servidor cruzando datos de reportes masivos (ej. Nombre del Cliente en 10,000 pedidos), el servidor de .NET minimiza los JOINs complejos. El API devuelve los UUIDs crudos al Frontend. La SPA en React intercepta estas respuestas y, en 0 milisegundos, consulta su propia base local (IndexedDB) para resolver e inyectar en pantalla los nombres humanos correspondientes (ej. resolver ClienteId hacia la Razón Social).

04.3 Almacenamiento Local y Consistencia Eventual (Offline)

Para cumplir con la directiva de operar sin internet en terminales de almacén y mostrador, el frontend en React implementa bases de datos locales usando IndexedDB gestionado a través de Dexie.js.

El Flujo de Consistencia Eventual y Sincronización

Ante la imposibilidad de garantizar el stock físico exacto sin red, MDX-Cloud aplica el patrón de "Consistencia Eventual con Compensación":

  1. Fase 1 (Caché Constante): Mientras haya red, React descarga y refresca silenciosamente los catálogos maestros y el último stock conocido en IndexedDB. Las búsquedas del usuario operan siempre contra este caché local a velocidad nativa.
  2. Fase 2 (Operación Offline): Si la red se cae, el sistema detecta el modo offline. El usuario puede seguir capturando pedidos o picking basándose en el inventario que existe en la caché. Estas transacciones reciben su UUIDv7 localmente y se encolan en tb_cola_tx_offline.
  3. Fase 3 (Sincronización e Idempotencia): Al recuperar la conexión, un Background Worker en React envía la cola de transacciones al servidor. El Backend recibe el UUIDv7; si detecta que ese ID ya existe (por un reintento de red), lo ignora asegurando la idempotencia.
  4. Fase 4 (Validación de Inventario Real): El Backend cruza las órdenes entrantes contra la "Realidad Física" actual en PostgreSQL.
    • Aprobado: Si hay stock disponible real, el pedido transacciona normalmente.
    • Rechazado (Compensación): Si el inventario se agotó mientras el dispositivo estaba offline, el pedido no se pierde. Pasa a un estado especial de "Requiere Revisión" y se emite una notificación al usuario indicándole que debe remover el producto agotado o sustituirlo para poder liberar la orden.

Sincronización en Tiempo Real y Deltas (WebSockets)

Para mantener a todas las terminales actualizadas instantáneamente y evitar colisiones de stock mientras tienen conexión a internet, la arquitectura emplea SignalR (.NET):

  • Túnel Permanente: Los clientes mantienen una conexión abierta bidireccional (WebSocket) con el backend.
  • Broadcast de Eventos: Si la "Terminal A" realiza una venta que disminuye el stock, el servidor dispara un evento (StockUpdated) hacia todos los demás dispositivos conectados en menos de 50 milisegundos.
  • Actualización Silenciosa: La "Terminal B" recibe el evento en segundo plano y actualiza directamente su IndexedDB local. El usuario de la Terminal B ve cómo el inventario en pantalla cambia mágicamente sin refrescar la página.
  • Recuperación por Deltas: Si un dispositivo pierde conexión y luego la recupera, antes de enviar sus pedidos offline, solicita al servidor un paquete de "Deltas" (los eventos de actualización que ocurrieron mientras estaba fuera de línea) para purgar su IndexedDB y tener la última verdad absoluta antes de procesar su propia cola.

04.4 Control de Versiones y Migraciones de Bases de Datos

(Última modificación: 2 de Junio de 2026)

Dado el entorno distribuido y la arquitectura Offline-First, la evolución del esquema de la base de datos se gestiona mediante procesos desacoplados y específicos para cada capa de persistencia. La regla arquitectónica establece que la base de datos local no es una réplica 1:1 de la base de datos central, sino un subconjunto estrictamente orientado a soportar el modo offline. Por ende, sus actualizaciones son independientes.

Base de Datos Central (PostgreSQL)

El esquema central es la única fuente de verdad y su evolución es dictada estrictamente por el código del backend.

  • Herramienta: Entity Framework Core Migrations (Code-First).
  • Proceso: Todo cambio estructural (tablas, columnas, índices, vistas en los 10 esquemas lógicos) debe modelarse primero como clases de dominio/entidades en C#. Las migraciones generan archivos inmutables que describen el cambio de esquema.
  • Despliegue: Queda prohibida la ejecución manual de comandos SQL en producción (DDL). Las migraciones se aplicarán automáticamente durante los despliegues automatizados (Pipeline CI/CD) o durante el arranque del contenedor de .NET, asegurando consistencia continua.

Base de Datos Local / Caché (IndexedDB)

El almacenamiento local en los dispositivos de los usuarios opera bajo la premisa absoluta de no perder datos no sincronizados ante un cambio de versión del sistema.

  • Herramienta: Versionado declarativo de Dexie.js.
  • Proceso: Al requerir almacenar nuevas entidades en modo offline (ej. un nuevo catálogo), se debe incrementar la versión de la base de datos local en la inicialización de Dexie (ej. db.version(2).stores(...)), preservando las declaraciones de las tablas previas intactas.
  • Despliegue: Al acceder el usuario a la aplicación web actualizada, Dexie captura el evento nativo onupgradeneeded del navegador y transforma el esquema local silenciosamente. Esto asegura que la "cola de transacciones" pendientes que el usuario pudiera tener almacenadas sin conexión no se destruya.