Cómo diseñar esquemas y modelos de datos escalables
En la última década, el diseño de modelos de datos ha dejado de ser una disciplina relativamente estable —centrada en normalización, integridad referencial y un puñado de patrones transaccionales— para convertirse en un arte mucho más situacional, moldeado por la nube, las arquitecturas distribuidas, el aumento del volumen de datos y el crecimiento explosivo de los casos de IA. Lo que antes era una discusión académica (“¿Debo normalizar o desnormalizar?”) hoy se ha transformado en decisiones tácticas que afectan directamente al coste de una plataforma, al rendimiento de los productos digitales y a la capacidad de un equipo de datos para operar con garantías.
El diseño de esquemas de datos representa una de las decisiones arquitectónicas más duraderas y costosas de modificar en cualquier plataforma. A diferencia del hardware o las herramientas, que pueden reemplazarse, un mal diseño de esquema puede perseguir a una organización durante años, limitando el rendimiento, incrementando costes operativos y dificultando la evolución del negocio.
Este capítulo explora cómo deben diseñarse los modelos de datos cuando el objetivo es escalar sin perder coherencia, control de costes ni visibilidad operativa. Para CIOs, arquitectos y equipos de ingeniería, este es un punto crítico: una mala decisión en el modelado puede provocar sobrecostes en cloud, cuellos de botella de latencia, dificultades regulatorias o una cadena interminable de parches y migraciones.
Aquí revisamos de forma pragmática tres aproximaciones que conviven en las plataformas modernas:
1. Normalización: útil para sistemas OLTP, cumplimiento regulatorio, auditoría y consistencia fuerte. Mantiene la integridad, facilita la gobernanza, reduce la duplicación… pero puede multiplicar el coste de lectura en sistemas distribuidos y encarecer operaciones en la nube.
2. Desnormalización: optimiza consultas analíticas, motores MPP y cargas de lectura masiva. Es la base de data warehouses, lakes y modelos read-optimized. Sin embargo, exige pipelines de refresco, cuidado con la calidad e introduce un riesgo potencial de inconsistencias.
3. Modelos por acceso: la evolución natural del diseño en microservicios y sistemas distribuidos. Son modelos que se construyen según las queries, los patrones de uso y la latencia objetivo. No se parte del dato, sino del acceso. Es el enfoque dominante en arquitecturas event-driven, domain-driven y plataformas digitales de gran escala.
Este capítulo proporciona un marco claro para elegir entre estas estrategias, combinarlas y evitar anti-patrones que ocurren repetidamente en organizaciones de todos los tamaños. Abordamos el diseño de modelos de datos desde una perspectiva práctica y orientada a la escala empresarial. La clave no está en seguir dogmas académicos, sino en comprender los trade-offs y diseñar esquemas que evolucionen con el negocio sin convertirse en cuellos de botella técnicos.
Además, se incluye un checklist operativo para validar modelos antes de aprobarlos y un caso práctico basado en una plataforma SaaS real.
Avance de algunas cuestiones importantes que iremos viendo:
- Los esquemas normalizados minimizan redundancia pero pueden generar consultas complejas y costosas en sistemas OLAP
- El modelado por acceso (access patterns) prioriza el rendimiento de lecturas específicas sobre la elegancia teórica
- La desnormalización estratégica reduce joins pero introduce complejidad en actualizaciones y riesgo de inconsistencias
- Los esquemas deben diseñarse considerando la escala futura, no solo los requisitos actuales
- La elección entre normalización y desnormalización depende fundamentalmente del ratio lectura/escritura de cada entidad
Introducción a la nueva década del modelado
Si uno revisa los manuales clásicos de diseño de bases de datos de los años 90 y 2000, la sensación es casi reconfortante: reglas claras, procedimientos establecidos, modelos entidad-relación, normalización progresiva… Una especie de ingeniería civil del dato en la que la estructura debía ser robusta, jerárquica y elegante. Durante mucho tiempo, ese mundo se mantuvo estable. Oracle, SQL Server, DB2 y MySQL marcaban la pauta. Las arquitecturas eran relativamente monolíticas, los sistemas de reporting vivían aislados y los volúmenes de información crecían de forma razonable.
Pero en la última década —prácticamente desde la irrupción del cloud público, Hadoop, los data lakes y los microservicios— ese mapa dejó de ser fiable. De pronto, los modelos de datos dejaron de ser planos arquitectónicos inmutables y pasaron a ser artefactos vivos, sometidos a presión constante: picos de tráfico, cargas masivas de eventos, auditorías, replicaciones geo-distribuidas, latencias estrictas exigidas por productos digitales, algoritmos que necesitan snapshots históricos al minuto, y un ecosistema de motores que se fragmenta en especializaciones (columnar, grafos, series temporales, vectores…).
En una conversación reciente con un CIO de una telco europea, él lo resumía así:
"Un modelo bien normalizado nos garantizaba integridad, sí. Pero no sobrevivió a los 1,2 millones de consultas por minuto de nuestros microservicios de facturación. La perfección académica nos estaba arruinando."
Y es que esa es la realidad a la que se enfrentan hoy los equipos de datos:
Un modelo puede ser perfecto… y aun así no servir en producción.
Cuando todo estalla a escala real
La escena se repite una y otra vez en sectores tan diversos como banca digital, retail masivo, e-commerce global o movilidad. Durante meses, los equipos diseñan un modelo centralizado, elegante, altamente normalizado, con claves bien definidas y jerarquías cuidadas. Se documenta, se valida, se revisa con negocio. Todo parece encajar.
Hasta que llega el tráfico real.
De pronto, consultas que antes parecían triviales se convierten en monstruos que ejecutan decenas de joins, disparan costos de I/O, rompen cachés y saturan el ancho de banda entre nodos distribuidos. En motores cloud-native, cada join mal planteado puede multiplicar la factura mensual. En entornos regulatorios como banca o farma, la presión por mantener trazabilidad choca con las necesidades de analítica en tiempo real. La integridad referencial, lejos de ser un salvavidas, puede transformarse en un freno.
Y aquí aparece una verdad incómoda:
El modelado orientado exclusivamente a la normalización ya no es suficiente para empresas que quieren operar a escala.
El ascenso del modelo por acceso
Al tiempo que las arquitecturas tradicionales comenzaban a tensarse, surgió un enfoque radicalmente distinto, que nació del mundo de microservicios, Amazon, Netflix, Uber y empresas similares: diseñar el modelo según las queries, no según los datos.
El access-modeling (o modelado por acceso) rompe las reglas clásicas. No parte de entidades ni de relaciones, sino de flujos, necesidades de latencia, casos de uso y cargas esperadas. Cada modelo se crea para resolver un patrón específico, sin aspirar a la universalidad.
Esto explica por qué sistemas modernos como DynamoDB, Bigtable, Elasticsearch o Snowflake impulsan patrones diferentes de modelado. A veces normalizar es lo correcto; a veces desnormalizar es esencial; a veces necesitas varios modelos coexistiendo para un mismo dominio.
El fin del modelo “único corporativo”
Muchos arquitectos senior han vivido la tentación: construir un gran modelo corporativo que lo describe todo, con el sueño de que todos los equipos trabajen sobre un “lenguaje universal”. Es una idea atractiva… hasta que la empresa entra en ritmo de innovación, despliegue continuo y múltiples productos de datos.
El modelo único se convierte en cuello de botella, fuente de discusiones eternas y barrera contra el cambio. La gobernanza se hace rígida, las migraciones rompen sistemas y la organización pierde velocidad.
En contraste, las empresas de alto rendimiento combinan:
-
modelos normalizados para transacciones y gobernanza,
-
modelos desnormalizados para analítica, reporting y cargas masivas,
-
modelos por acceso para microservicios, productos digitales, APIs y motores de recomendación.
El papel del CIO y del Data Architect en esta nueva era
Para el CIO moderno, el modelado de datos dejó de ser una decisión técnica delegable. Hoy afecta:
-
al FinOps (cuánto cuesta cada consulta),
-
al DataOps (cómo se versiona y despliega un esquema),
-
a la experiencia del usuario final (latencia),
-
a la calidad de los KPIs y dashboards,
-
a la IA generativa y predictiva,
-
y a la capacidad de la organización para escalar productos globalmente.
Los arquitectos de datos, por su parte, necesitan una visión menos dogmática y más contextualizada. Modelar hoy implica entender motores distribuidos, patrones de acceso, comportamiento del almacenamiento en la nube, estructuras columnar vs row-based, materializaciones, vector stores, CDC, particionado, modelos de consistencia, SLOs, SLA de latencia, cargas mixtas y costes de consulta.
Por eso este capítulo no pretende “dictar” la mejor opción, sino ofrecer un marco profesional y accionable para escoger la estrategia adecuada según cada escenario.
Los fundamentos que olvidamos (pero no deberíamos)
Normalización: más que una reliquia académica
La normalización ha sido, durante décadas, la brújula del diseño de bases de datos relacionales. Cualquier ingeniero con experiencia reconoce las siglas 1FN, 2FN, 3FN, BCNF… y podría explicar de memoria las reglas que garantizan integridad, ausencia de duplicidades y un modelo “elegante”. Sin embargo, en la práctica actual —con motores distribuidos, cargas masivas, microservicios y despliegues en la nube— estos principios requieren reinterpretarse. No se trata de desecharlos, sino de aplicar normalización con inteligencia contextual.
Lo que la normalización hace realmente (cuando funciona bien)
Cuando Codd formalizó las formas normales en los años 70, no estaba creando un ejercicio teórico. Estaba resolviendo problemas reales de redundancia, anomalías de actualización y desperdicio de almacenamiento en sistemas donde cada byte contaba. Hoy, con almacenamiento barato y abundante, es tentador considerar la normalización como un vestigio innecesario. Esta es una simplificación peligrosa.
La normalización sigue siendo relevante, pero su valor ha cambiado. Ya no normalizamos principalmente para ahorrar espacio, sino para mantener una única fuente de verdad y simplificar la lógica de escritura. En sistemas transaccionales (OLTP), donde múltiples procesos actualizan datos concurrentemente, la normalización reduce dramáticamente la superficie de posibles inconsistencias.
Consideremos un esquema de comercio electrónico. Si almacenamos el nombre del cliente y su dirección tanto en la tabla de pedidos como en la de clientes, cada actualización de dirección requiere modificar múltiples registros. No solo duplicamos el trabajo de escritura: introducimos una ventana para que los datos queden desincronizados. ¿Qué dirección es la correcta cuando difieren? La normalización elimina esta ambigüedad.
En sistemas OLTP —como un core bancario, un ERP, un sistema de facturación o un CRM transaccional— la normalización es un pilar fundamental. Sus beneficios siguen siendo válidos:
-
Integridad fuerte: las reglas de normalización obligan a que los datos no se dupliquen innecesariamente, evitando que existan dos verdades distintas en diferentes tablas.
-
Menos anomalías: se elimina el riesgo de que la actualización de un registro deje datos inconsistentes en otras partes del modelo.
-
Gobernanza más clara: un dato tiene un hogar único, lo que facilita auditorías, trazabilidad y cumplimiento regulatorio.
-
Modelos duraderos: los cambios estructurales son más fáciles de controlar cuando las dependencias son explícitas y bien formadas.
Para sistemas universalmente críticos —por ejemplo, la base de clientes de un banco, los contratos de una aseguradora, los asientos contables o la gestión de inventario— esto sigue siendo imprescindible.
Las formas normales en la práctica empresarial:
- Primera forma normal (1NF): Eliminar grupos repetitivos. Parece básico, pero aún encontramos campos como "teléfonos" conteniendo múltiples números separados por comas. Esto imposibilita búsquedas eficientes y validaciones robustas.
- Segunda forma normal (2NF): Eliminar dependencias parciales de claves compuestas. En la práctica, significa que cada atributo debe depender de toda la clave primaria, no solo de parte de ella.
- Tercera forma normal (3NF): Eliminar dependencias transitivas. Si el código postal determina la ciudad, y la ciudad está en el registro, tenemos redundancia. En sistemas transaccionales, 3NF suele ser el equilibrio óptimo entre coherencia y practicidad.
- Boyce-Codd (BCNF) y superiores: Útiles en casos específicos, pero raramente necesarias en entornos empresariales. Perseguir estas formas puede complicar innecesariamente el modelo sin beneficios tangibles.
Pero la normalización, aplicada sin conciencia real del contexto técnico, puede convertirse en un obstáculo importante en un entorno moderno.
Cuando la normalización empieza a romperse (o a costar demasiado)
La mayoría de problemas con modelos normalizados no son teóricos; son operativos. Y casi todos surgen en cuanto el sistema escala más allá del tráfico esperado inicialmente. Los síntomas se repiten:
-
Exceso de joins: consultas aparentemente simples que requieren unir cinco, siete o diez tablas.
-
Latencias inestables: en motores distribuidos, cada join entre nodos implica shuffles de datos que disparan la latencia.
-
Costes cloud impredecibles: en plataformas como BigQuery, Snowflake o Redshift, joins mal diseñados pueden multiplicar el coste de una consulta por 10 o 50.
-
Cargas mixtas incompatibles: un modelo muy normalizado funciona bien para OLTP… pero no para OLAP, analítica near-real-time o cargas basadas en series temporales.
-
Problemas con particionado: normalizar sin pensar en la clave de partición es uno de los errores más comunes en bases de datos distribuidas.
Esto es especialmente problemático cuando el modelo normalizado se convierte en un “modelo central” del que dependen microservicios, pipelines, dashboards y APIs. En esos casos, cada modificación genera fricción estructural que se propaga por toda la plataforma.
La normalización en motores cloud-native
Una de las consecuencias del paradigma cloud es que operar un modelo altamente normalizado puede ser más caro que almacenarlo de forma ligeramente redundante. El coste de almacenamiento en objetos o bases MPP suele ser muy bajo; el coste de lectura, en cambio, puede ser alto y estar directamente ligado a:
-
bytes escaneados,
-
número de particiones tocadas,
-
nº de nodos involucrados,
-
tamaño del shuffle,
-
cantidad de joins.
Por eso, para cargas analíticas, la normalización estricta rara vez es la opción óptima. Es una de las razones por las que muchos data warehouses modernos (Snowflake, BigQuery, Azure Synapse, Redshift, Databricks SQL) recomiendan modelos denormalizados o semidenormalizados, especialmente en capas de consumo.
La normalización en bases distribuidas OLTP
En motores como Spanner, YugabyteDB, CockroachDB, TiDB o incluso MongoDB en modo transaccional, la normalización debe convivir con:
-
particionado automático o manual,
-
balanceo de nodos,
-
replicación sincrónica o cuasi-sincrónica,
-
consistencia configurada,
-
latencias interzona o interregión.
La regla práctica es que cada join puede convertirse en una operación distribuida… salvo que las tablas estén particionadas por la misma clave. Así, la normalización solo funciona bien si se diseña:
-
pensando en la clave de partición,
-
anticipando la cardinalidad y distribución del dato,
-
garantizando que las consultas más frecuentes permanecen “locales” dentro de una misma partición o shard.
Ignorar esto provoca uno de los anti-patrones más comunes: un modelo aparentemente perfecto que en producción requiere casi siempre consultas cross-shard, carísimas en tiempo y recursos.
Ejemplos sectoriales donde la normalización sigue siendo crítica
Banca:
Un error en un modelo normalizado puede significar una discrepancia contable. Los libros mayores, los asientos y los movimientos deben seguir normalizados. Pero incluso la banca moderna empieza a usar desnormalización táctica en motores de fraude, scoring o analítica regulatoria.
Telecomunicaciones:
Los sistemas de BSS/OSS mantienen normalizados los catálogos, tarifas y productos. Pero la red de eventos (CDRs) se desnormaliza intensivamente para cálculos de consumo y facturación.
Retail:
Inventarios, proveedores y logística suelen estar normalizados. Sin embargo, la analítica de ventas, el pricing dinámico y la personalización funcionan con modelos muy desnormalizados.
Conclusión operativa: normalizar, pero con conciencia de escala
La normalización sigue siendo un arma poderosa. Pero ya no es un dogma. En sistemas distribuidos y arquitecturas modernas, normalizar debe ser una decisión consciente, combinada con:
-
estrategias de particionado,
-
proyecciones o materializaciones,
-
modelos de lectura,
-
y mecanismos de refresco o sincronización.
Una base de datos normalizada es excelente para escribir datos correctamente, pero no siempre es la mejor para leerlos a escala.
Desnormalización para rendimiento y analítica
Si la normalización es el cimiento clásico de los sistemas transaccionales, la desnormalización es la piedra angular de prácticamente todos los sistemas analíticos modernos. Durante años, la desnormalización tuvo mala reputación: parecía un “atajo”, un “parche” frente a modelos bien diseñados. Pero el crecimiento masivo del dato, el auge del análisis en tiempo real y los motores columnares y distribuidos han consolidado la desnormalización como un patrón estándar —y, en muchos casos, imprescindible—.
En la práctica, hoy ninguna arquitectura de datos escalable funciona sin algún nivel de desnormalización. La clave no es evitarla, sino saber dónde, cómo y con qué mecanismos de control aplicarla.
Cuando la desnormalización no solo es útil, sino necesaria
El primer motivo por el que la desnormalización se vuelve crítica es muy simple: cuesta muchísimo menos leer datos que unirlos. En un data warehouse o un motor columnar, cada join implica shuffles de datos, expansión de conjuntos, redes saturadas y tiempo desperdiciado.
No es casualidad que Snowflake, BigQuery, Spark SQL, Databricks, Redshift y prácticamente todos los motores analíticos recomienden explícitamente:
-
usar tablas anchas (wide tables),
-
materializar vistas,
-
crear modelos por métrica o caso de uso,
-
evitar joins innecesarios,
-
y anticipar la cardinalidad desde la capa de ingestión.
El objetivo es reducir trabajo computacional y acelerar las consultas. Esto se vuelve especialmente importante en motores serverless donde el coste está directamente ligado a lo que se escanea.
El diseño read-optimized: el patrón dominante en analítica
La desnormalización permite crear lo que se conoce como read-optimized data, modelos pensados para servir una consulta —o un conjunto de ellas— de la forma más rápida posible, sin necesidad de transformaciones adicionales.
Aquí aparecen varios patrones habituales:
Tablas wide (anchas)
Muy usadas en BigQuery o Snowflake. Toda la información relevante para una entidad se coloca en una sola tabla, incluso a costa de duplicar datos.
Ejemplo:
Una tabla orders_wide que incluye datos del pedido, del cliente, del producto, de la campaña, y metadatos adicionales.
En un entorno normalizado, esto implicaría mínimo unas 6–8 joins.
Materializaciones periódicas
Se usan para “precomputar” resultados.
Ejemplos típicos:
-
ventas diarias agregadas,
-
métricas de churn actualizadas cada hora,
-
snapshots de estado para modelos de ML.
Herramientas como dbt, Snowflake Tasks, BigQuery Scheduled Queries o Databricks Workflows facilitan este patrón.
Tablas derivadas de distinta granulidad
Muy comunes en retail y e-commerce.
Por ejemplo:
-
ventas por ticket,
-
ventas por cliente,
-
ventas por día,
-
ventas por categoría.
Todas derivadas del mismo fact principal, pero optimizadas según el tipo de consulta.
Modelos estrella y copo de nieve
Aunque parecen conceptos clásicos de Kimball, en realidad siguen siendo desnormalizaciones tácticas. Las dimensiones duplican información para evitar el coste de joins de varios niveles.
Riesgos y costes de la desnormalización
No hay magia: la desnormalización aporta velocidad de lectura, pero introduce nuevas obligaciones operativas. Y aquí es donde muchas empresas fallan.
1. Riesgo de inconsistencias
Cuando una entidad aparece en múltiples modelos, mantener la coherencia depende del pipeline de transformación. Si este falla, aparecen divergencias sutiles entre tablas —uno de los problemas más difíciles de detectar en analítica—.
2. Lógica duplicada
Si varias tablas wide recrean la misma lógica, la deuda técnica crece. Es un problema común en compañías con muchos analistas SQL que crean sus propios modelos.
3. Sobrecoste de almacenamiento
El almacenamiento es barato, sí. Pero no gratis. Un exceso de duplicación puede multiplicar el tamaño del warehouse, especialmente si se guardan snapshots históricos.
4. Pipelines más complejos
Cada modelo derivado necesita refresco, monitorización, testeo y versionado.
Si se generan decenas de modelos por acceso sin control, el coste de mantenimiento se dispara.
Desnormalización operada con calidad: cómo evitar el caos
Las organizaciones que dominan este enfoque no improvisan. Adoptan mecanismos explícitos de control:
1. Data contracts y definiciones centralizadas
El dato fuente debe estar normalizado y bien gobernado. La desnormalización se apoya en un contrato estable, no en reglas ad hoc.
2. Lineage automatizado
Para identificar qué modelos dependen de qué otros. Esto evita romper dashboards al cambiar un campo upstream.
Herramientas comunes: OpenLineage, Amundsen, DataHub, Collibra, Alation.
3. Testeo sistemático
Especialmente en dbt, donde se testean:
-
unicidad,
-
integridad,
-
valores esperados,
-
consistencia entre modelos.
4. Observabilidad de datos
Alertas cuando cambia la distribución estadística, la cardinalidad, el volumen o la completitud.
Soluciones típicas: Monte Carlo, Soda, Great Expectations, Databand.
5. Cadencia y SLAs de refresco
Una materialización tiene que refrescarse a tiempo. Si no, se vuelve inútil.
Los SLOs de frescura son tan importantes como los SLOs de computación.
Ejemplos sectoriales donde la desnormalización aporta ventaja competitiva
E-commerce global
Las recomendaciones, las búsquedas y el análisis de comportamiento requieren datasets con perfiles completos del usuario y del producto en una sola tabla, para latencias por debajo de los 200–300 ms.
Logística y movilidad
Los algoritmos de rutas, tiempos estimados de entrega y optimización de flotas operan mejor con datos enriquecidos, no con joins en tiempo real.
SaaS de alto volumen
Los equipos de producto necesitan métricas que se actualizan cada pocos minutos: cohortes, funnels, DAU, MAU, retención, churn, engagement.
Todo esto funciona con modelos desnormalizados y materializaciones.
Finanzas modernas
La analítica de riesgo, detección de fraude o scoring de crédito necesitan vistas agregadas y enriquecidas con datos históricos y proyecciones.
Conclusión operativa: desnormalizar donde la lectura importa más que la escritura
La desnormalización es una herramienta potentísima cuando:
-
el acceso es intensivo,
-
la latencia importa,
-
el coste de los joins es significativo,
-
se necesita acelerar el análisis,
-
o se deben soportar cargas MPP masivas.
Pero debe hacerse con disciplina operativa, no como reacción improvisada ante un modelo lento.
En la práctica moderna, las arquitecturas más escalables combinan normalización en la capa de escritura y desnormalización en la capa de lectura.
El “modelo por acceso”: diseñar para la query, no para los datos
Durante décadas, los arquitectos de datos partían de una premisa aparentemente indiscutible: primero se modelan las entidades, sus relaciones y las reglas del negocio; después se piensan las consultas. Pero en la última década —con la explosión del cloud, las arquitecturas distribuidas y la cultura de microservicios— este proceso se ha invertido por completo en muchas organizaciones de alto rendimiento.
El enfoque tradicional de modelado de datos comienza con el análisis de entidades y relaciones del dominio del negocio, produciendo un modelo entidad-relación (ER) que luego se traduce a tablas normalizadas. Este enfoque funciona admirablemente para sistemas OLTP donde las transacciones son variadas e impredecibles.
Sin embargo, en sistemas OLAP, data warehouses, y especialmente en bases de datos NoSQL, este enfoque resulta insuficiente. Aquí, el rendimiento depende críticamente de alinear la estructura de datos con los patrones de acceso dominantes.
El modelado por acceso invierte la ecuación: comenzamos identificando las consultas críticas del negocio, sus frecuencias relativas, y sus requisitos de latencia. Luego diseñamos esquemas que optimizan específicamente estos accesos, incluso si esto significa redundancia controlada o estructuras aparentemente "desordenadas".
Hoy, los modelos más eficientes en plataformas globales no se diseñan a partir del dato. Se diseñan a partir del acceso. Por eso se habla de access-based modeling: un enfoque pragmático, orientado a necesidades reales y fundamentado en el comportamiento esperado del sistema.
El modelo por acceso no pretende ser universal. No busca representar todo el negocio ni crear un “modelo corporativo maestro”. Su objetivo es simple, pero poderoso:
optimizar al máximo las consultas críticas del sistema, aunque para ello haya que renunciar a la elegancia teórica o a la coherencia global.
Por qué surge este enfoque: la presión de la latencia y el coste
Los sistemas modernos funcionan bajo una presión doble:
-
Alta latencia no es aceptable.
Un usuario que espera 300 ms más en un checkout abandona la compra.
Un microservicio que tarda 40 ms más puede provocar cascadas de fallos. -
El coste por query importa.
En muchos motores cloud, cada join cuesta dinero.
Cada shuffle, cada tabla cruzada, cada lectura no particionada tiene impacto económico real.
En este contexto, diseñar un modelo abstracto, normalizado y elegante deja de ser viable. El rendimiento se vuelve determinante… y el rendimiento depende del patrón de acceso.
Cómo funciona realmente el modelo por acceso
El proceso es casi el inverso al tradicional:
-
Primero se identifican las queries críticas.
-
Luego se estudian sus patrones de acceso: qué leen, cuánto leen, con qué frecuencia y bajo qué latencias.
-
Después se evalúan las cargas concurrentes, los picos, la distribución geográfica, el tipo de indexación necesario.
-
Finalmente se diseña un modelo que haga esas consultas triviales, rápidas y eficientes.
Si el modelo resultante parece “anti-normalizado”, poco elegante o duplicado, probablemente sea correcto.
El criterio es operativo, no académico.
El origen del “modelo por acceso”: microservicios, DDD y sistemas event-driven
Hay tres movimientos arquitectónicos clave que consolidaron este enfoque:
1. Microservicios y bounded contexts (DDD)
Cada microservicio debe tener su propio modelo interno, adaptado a su dominio.
Ese modelo:
-
no necesita ser igual al de otros servicios,
-
no tiene por qué estar normalizado,
-
no representa el modelo corporativo global,
-
sirve solo para su propósito.
Esto rompe la idea del “modelo único” y obliga a pensar en modelos especializados.
2. Bases NoSQL y motores distribuidos
Sistemas como DynamoDB, Bigtable, Cassandra o MongoDB empujan al diseñador a crear modelos basados en patrones de acceso, porque:
-
los joins no existen,
-
la latencia depende de la forma del documento o la clave,
-
la consulta define la estructura del dato.
En DynamoDB, por ejemplo, diseñar sin pensar en accesos es directamente imposible.
3. Arquitecturas orientadas a eventos (Kafka, Pulsar)
En sistemas event-driven, los datos no se almacenan una sola vez. Se materializan múltiples veces:
-
modelos de lectura para microservicios,
-
agregaciones para analítica,
-
proyecciones para dashboards,
-
vistas especializadas.
Cada uno de estos modelos está optimizado para un acceso específico.
Patrones habituales del modelo por acceso
1. Write model + múltiples read models
Patrón muy común en arquitecturas CQRS.
El modelo de escritura suele estar normalizado o altamente estructurado, pero los modelos de lectura están desnormalizados y adaptados a cada consumidor.
2. Materializaciones dirigidas por necesidad
Si un sistema debe responder a una pregunta en 100 ms, se crea un modelo cuyo propósito es permitir esa respuesta inmediata, sin joins.
3. Modelos orientados a API
Cuando una API pública o interna necesita devolver un payload complejo, el modelo se construye directamente para servir ese payload sin composición en tiempo de ejecución.
4. Proyecciones para machine learning
Los modelos de ML trabajan con datasets enriquecidos y consolidados.
Las “feature tables” suelen estar diseñadas explícitamente para su acceso, no para su consistencia semántica.
Ventajas del modelo por acceso
-
Latencia mínima.
Se diseña para consultas rápidas, incluso bajo carga masiva. -
Previsibilidad operativa.
Es más fácil garantizar SLAs cuando las consultas son triviales. -
Escalado más barato.
Menos joins significa menos coste en cloud y menor saturación en horas pico. -
Flexibilidad para equipos de producto.
Cada dominio puede evolucionar sin romper otros.
Riesgos y anti-patrones
Como cualquier técnica potente, mal aplicada puede convertirse en un desastre:
1. Caos de modelos descoordinados
Si cada equipo crea su propio modelo sin gobernanza, se genera fragmentación, duplicación de lógica y pérdida de coherencia.
2. Pérdida de trazabilidad
Si los datos se materializan diez veces sin lineage, es difícil saber qué versión es la correcta.
3. Falta de consistencia cross-dominio
Cada modelo es válido en su contexto, pero puede contradecir a otro si no existe un source-of-truth aguas arriba.
4. Complejidad en las migraciones
Cambiar un modelo upstream implica tocar varias materializaciones downstream.
Casos reales donde el modelo por acceso es el rey
Motores de recomendación
Necesitan un dataset de usuario+producto preoptimizado.
Los joins en tiempo real son imposibles.
Sistemas de fraude
La decisión debe tomarse en 30–80 ms.
Solo funcionan con modelos por acceso, enriquecidos previamente.
Búsquedas y catálogos
Sistemas como Elasticsearch o Solr viven de la desnormalización extrema y de la indexación dirigida por consulta.
Microservicios críticos
Pedidos, inventarios, pagos, autenticación…
Todos ellos optimizan su modelo según la carga.
Patrones de acceso en la práctica
En una plataforma de e-commerce, identifiquemos patrones típicos:
Patrón 1: Vista de detalle de producto
- Frecuencia: Miles de consultas por segundo
- Latencia objetivo: <50ms
- Datos requeridos: Información del producto, precio actual, inventario, reseñas agregadas, productos relacionados
Patrón 2: Historial de pedidos del cliente
- Frecuencia: Cientos de consultas por segundo
- Latencia objetivo: <200ms
- Datos requeridos: Lista de pedidos con detalles básicos, ordenados cronológicamente
Patrón 3: Análisis de tendencias de ventas
- Frecuencia: Decenas de consultas por hora
- Latencia objetivo: <5s
- Datos requeridos: Agregaciones complejas por múltiples dimensiones (tiempo, categoría, región)
Un modelo normalizado trataría estos accesos por igual. Un modelo por patrones de acceso reconoce que no todos los bytes son iguales: los que se leen miles de veces por segundo merecen optimización agresiva.
Para el Patrón 1, podríamos mantener una vista materializada o tabla desnormalizada que pre-join toda la información, evitando 5-7 joins en tiempo de consulta. Para el Patrón 3, podríamos mantener cubos pre-agregados que se actualizan cada hora, evitando escaneos completos de tablas transaccionales.
Conclusión operativa: el acceso manda
El modelo por acceso no compite con la normalización ni con la desnormalización.
Es el puente entre ambos: un enfoque pragmático adaptado a la realidad del negocio y de los sistemas distribuidos.
Su esencia es sencilla y poderosa:
Diseña para la query. Optimiza para la latencia. Paga el coste de duplicación si es necesario.
Pero nunca sacrifiques el rendimiento por elegancia del modelo.
Diseñar modelos para escalado en la práctica: OLTP vs OLAP, stream vs batch
A estas alturas del capítulo ya ha quedado claro que normalizar, desnormalizar o modelar por acceso no son decisiones absolutas, sino estrategias situacionales. Pero para aplicarlas correctamente hace falta entender el contexto operativo de cada sistema. En arquitectura de datos esto se resume en un principio fundamental:
No existe “un modelo”; existen modelos adecuados a cargas específicas.
Las cargas, en plataformas modernas, suelen dividirse en dos grandes grupos:
OLTP (procesamiento transaccional online) y OLAP (procesamiento analítico).
A esto se suma una segunda dimensión crucial: batch vs stream.
De estas combinaciones emergen cuatro cuadrantes, cada uno con requisitos propios que afectan directamente al diseño del modelo.
1. Modelado OLTP: la tierra de la consistencia y la baja latencia
Los sistemas OLTP son aquellos que procesan operaciones individuales, pequeñas y críticas: pagos, pedidos, actualizaciones de inventario, autenticación, reservas de vuelos, movimientos contables.
En OLTP, el diseño del modelo debe priorizar:
-
consistencia,
-
integridad,
-
aislamiento,
-
latencia mínima,
-
capacidad de actualización concurrente.
Aquí, la normalización sigue siendo reina, pero con matices:
Normalización con particionado en mente
En bases distribuidas, normalizar no es difícil. Lo difícil es normalizar sin crear joins cross-shard. Por tanto:
-
Las tablas críticas deben compartir clave de partición.
-
Los accesos frecuentes deben mantenerse locales dentro del shard.
-
Los campos de alta cardinalidad deben evaluarse cuidadosamente.
En motores como CockroachDB, Spanner o Yugabyte, saltarse estas reglas significa introducir retrasos invisibles que solo aparecen bajo carga real.
El riesgo del “modelo único OLTP corporativo”
Uno de los errores más comunes en grandes empresas es imponer un modelo centralizado para todos los dominios OLTP. Esto genera:
-
cuellos de botella en cambios,
-
modelos sobredimensionados,
-
esquemas rígidos que no evolucionan,
-
equipos que bordean el modelo creando copias internas “temporales” que acaban siendo permanentes.
Las organizaciones de más éxito dividen el OLTP en dominios con modelos propios, gobernados, pero independientes.
2. Modelado OLAP: la tierra de la agregación masiva
Ralph Kimball popularizó el modelado dimensional específicamente para data warehouses. Su genialidad fue reconocer que los usuarios de negocio piensan en términos de métricas (hechos) y contexto (dimensiones), no en términos de entidades normalizadas.
Esquemas estrella vs copo de nieve:
El esquema estrella mantiene una tabla de hechos central rodeada de tablas de dimensiones desnormalizadas. Es intuitivo, rápido de consultar, y fácil de entender para analistas de negocio. El esquema copo de nieve normaliza las dimensiones, reduciendo espacio pero añadiendo joins.
En la práctica empresarial moderna, con almacenamiento económico y motores columnares optimizados, los esquemas estrella dominan. Los copos de nieve sobreviven principalmente en sistemas legacy o cuando las dimensiones son extremadamente grandes y raramente consultadas completamente.
Tablas de hechos: el núcleo del warehouse
Una tabla de hechos contiene métricas cuantificables del negocio (ventas, visitas, transacciones) junto con claves foráneas a dimensiones. Existen tres tipos principales:
- Hechos transaccionales: Capturan eventos individuales (cada venta). Crecen indefinidamente y requieren particionado cuidadoso.
- Hechos periódicos: Snapshot del estado en intervalos regulares (inventario diario). Útiles cuando el estado intermedio importa más que las transacciones individuales.
- Hechos acumulativos: Rastrean procesos con múltiples etapas (pipeline de ventas). Filas se actualizan a medida que el proceso avanza.
Dimensiones conformadas: la clave de la coherencia
Uno de los mayores desafíos en organizaciones con múltiples data marts es la proliferación de definiciones inconsistentes. ¿Qué es un "cliente activo"? ¿Cómo se categoriza un producto? Las dimensiones conformadas establecen definiciones maestras compartidas entre todos los data marts, asegurando que los informes sean comparables.
Implementar dimensiones conformadas requiere gobernanza firme. Necesitamos un equipo que posea estas definiciones, procesos para evolucionar dimensiones sin romper dashboards existentes, y herramientas que detecten desviaciones.
En OLAP, el objetivo es opuesto al de OLTP:
-
procesar grandes volúmenes,
-
optimizar agregaciones,
-
trabajar en columnar,
-
minimizar el coste por escaneo,
-
admitir flexibilidad estructural,
-
responder rápidamente a consultas complejas.
Por eso OLAP favorece:
-
desnormalización,
-
tablas wide,
-
materializaciones,
-
organización por particiones de tiempo,
-
modelos estrella.
Desnormalizar es prácticamente obligatorio
Porque:
-
una join en OLAP implica shuffles,
-
el coste se mide en bytes leídos,
-
la latencia depende del tamaño del dataset,
-
la carga es masiva y concurrente.
En warehousing moderno, un modelo normalizado sin vistas materializadas suele ser inviable.
El truco OLAP: mover trabajo a ingestión
Las organizaciones que dominan OLAP desplazan parte de la transformación hacia ingestión o hacia un paso intermedio ETL o ELT, para que el warehouse pueda responder más rápido y con menos coste.
3. Modelado para Streaming: consistencia eventual, snapshots y “modelos vivos”
El streaming introduce una complejidad completamente distinta: los datos cambian continuamente y los modelos deben reflejar esos cambios casi en tiempo real. Kafka, Pulsar o Kinesis funcionan bajo la premisa de que:
-
los datos viajan en eventos,
-
el estado se reconstruye,
-
lo importante es la actualización incremental,
-
las proyecciones se regeneran continuamente.
Aquí aparecen tres patrones clave:
1. Modelos derivados continuamente (proyecciones)
Los consumidores de eventos mantienen su propio estado reconstruido, optimizado para su acceso.
Esto refuerza el enfoque de múltiples modelos por dominio.
2. Snapshots periódicos
Algunos sistemas combinan un snapshot base (batch) con actualizaciones en streaming (CDC, eventos).
Es el modelo dominante en:
-
catálogos,
-
sistemas de fraude,
-
dashboards near-real-time,
-
IoT.
3. Actualizaciones idempotentes y ordenadas
El orden importa. El manejo de duplicados también.
El modelo debe construirse pensando en:
-
claves de negocio estables,
-
orden lógico (timestamps, offsets),
-
estrategias para late data.
4. Modelado Batch: la estabilidad como ventaja
El batch, lejos de desaparecer, sigue siendo la espina dorsal de:
-
cierres contables,
-
cargas financieras,
-
procesos regulatorios,
-
preparación de datasets para ML,
-
cálculo de métricas diarias.
En el batch, el diseño favorece:
-
modelos amplios,
-
procesados determinísticos,
-
transformaciones complejas,
-
tablas de diferentes granulidades.
A diferencia del streaming, se puede asumir:
-
orden definido,
-
repetición sin riesgo,
-
control total de ventanas,
-
lógica más pesada.
Por eso, incluso organizaciones que trabajan en real-time procesan snapshots en batch para auditoría o reconciliación.
El trilema del modelado moderno: latencia, coste y frescura
Todo diseño de modelo de datos en 2025 se reduce a balancear tres elementos:
1. Latencia
¿Necesitamos responder en milisegundos, segundos o minutos?
2. Coste
¿Cuánto cuesta escanear este modelo 10.000 veces al día?
3. Frescura
¿Es necesario que el dato esté actualizado al segundo… o basta con un corte diario?
El modelo que sirve para consultas inmediatas casi nunca es el más barato o el más consistente.
El modelo barato rara vez es el más fresco.
El modelo más consistente puede ser inviable en cloud por coste.
Por eso las arquitecturas modernas no se construyen sobre un modelo, sino sobre una arquitectura de modelos, cada uno optimizado para su cuadrante OLTP/OLAP/stream/batch.
Conclusión operativa: diseñar para el cuadrante correcto
El éxito del modelado a escala depende de:
-
identificar el cuadrante operativo,
-
elegir normalización/desnormalización según ese cuadrante,
-
crear modelos por acceso para los casos críticos,
-
mantener trazabilidad entre ellos,
-
y aceptar que coexistirán múltiples representaciones del mismo dominio.
En otras palabras:
No diseñes un modelo universal. Diseña el modelo adecuado para cada carga.
La escala no perdona a los modelos genéricos.
Decisiones de diseño para modelos escalables
Particionado y sharding: el fundamento de la escala horizontal
Ningún servidor individual, por potente que sea, puede manejar infinitamente datos o consultas. El particionado divide los datos en segmentos manejables, permitiendo escalado horizontal y consultas paralelas.
Particionado vertical: Dividir tablas por columnas. Útil cuando algunas columnas se acceden frecuentemente y otras raramente. Los motores columnares (Redshift, BigQuery) hacen esto automáticamente, pero en bases de datos row-store podemos separar manualmente datos "calientes" de "fríos".
Particionado horizontal: Dividir tablas por filas según algún criterio (tiempo, geografía, rango de IDs). Es el más común y efectivo para tablas masivas.
La elección de la clave de particionado es crítica. Una mala elección genera particiones desbalanceadas (algunas enormes, otras minúsculas), arruinando el rendimiento. Las claves ideales:
- Aparecen en la mayoría de consultas críticas (evitando un scan de todas las particiones)
- Distribuyen datos uniformemente
- Se alinean con patrones de archivo/purga (borrar particiones completas es más eficiente que borrados selectivos)
Para datos temporales, particionar por fecha es casi siempre correcto. Para datos geográficos distribuidos, mejor particionar por región. Para tenants en sistemas multi-tenant, particionar por tenant_id.
Sharding: particionado distribuido
Mientras el particionado típicamente ocurre dentro de una base de datos, el sharding distribuye datos entre múltiples servidores independientes. Esto permite un escalado casi ilimitado pero introduce una complejidad operativa significativa.
Las estrategias de sharding incluyen hash-based (distribución uniforme pero con consultas multi-shard costosas), range-based (queries por rango eficientes pero con riesgo de hotspots), y geographic (localidad de datos pero complejidad en las consultas globales).
Tipos de datos: pequeñas decisiones, grandes consecuencias
La elección de tipos de datos parece mundana pero impacta profundamente el en rendimiento y los costes a escala. Un campo CHAR(1000) cuando CHAR(50) es suficiente desperdicia memoria, degrada cache hit rates, y ralentiza transferencias de red.
Reglas prácticas:
- Usar enteros en lugar de strings para claves cuando sea posible (4 bytes vs decenas)
- Preferir tipos nativos de fecha/hora sobre strings formateados
- Considerar enums o tipos custom para campos con valores limitados
- En sistemas columnares, la cardinalidad importa: los campos con alta cardinalidad comprimen mal
El dilema de los UUIDs vs enteros secuenciales:
Los UUIDs ofrecen unicidad global sin coordinación, útiles en sistemas distribuidos o cuando se fusionan datos de múltiples fuentes. Pero consumen 16 bytes vs 4-8 bytes de enteros, degradan los índices B-tree (inserción aleatoria vs secuencial), y complican el debugging.
Los enteros secuenciales son eficientes pero revelan información (volumen de entidades, timing de creación) y requieren coordinación en sistemas distribuidos. Una solución intermedia: UUIDs generados de forma ordenada (UUID v7) que combinan timestamp con aleatoriedad, preservando así la eficiencia de inserción.
Índices: el multiplicador de rendimiento más incomprendido
Los índices son el mecanismo más potente para acelerar consultas, pero cada índice tiene un coste en escritura y almacenamiento. El arte está en identificar los índices que ofrecen máximo beneficio con mínimo overhead.
Índices que casi siempre necesitarás:
- Claves primarias (automáticas en la mayoría de sistemas)
- Claves foráneas en joins frecuentes
- Columnas en cláusulas WHERE de consultas críticas
- Columnas en ORDER BY de listados paginados
Índices compuestos: potentes pero peligrosos
Un índice en (columna_a, columna_b) no es intercambiable con índices individuales en ambas columnas. El orden importa: este índice acelera consultas con filtros en ambas columnas, o solo columna_a, pero no solo columna_b.
La regla general: ordenar columnas de más selectiva (filtra más filas) a menos selectiva. Pero esto se complica con cardinalidades cambiantes y patrones de consulta variados.
Índices de cobertura: el rendimiento extremo
Un índice de cobertura incluye todas las columnas necesarias para una consulta, permitiendo responder sin acceder a la tabla principal. Esto puede reducir I/O en órdenes de magnitud para consultas frecuentes, pero multiplica el tamaño del índice.
Usar covering indexes para las consultas más críticas (top 5% por frecuencia o importancia), no para todo.
Esquemas evolutivos: diseñar para el cambio
La inevitabilidad del cambio
El único diseño de esquema que no necesitará cambiar es aquel de un sistema que nunca se usa. Todo lo demás evoluciona: nuevos requisitos del negocio, integraciones inesperadas, regulaciones emergentes, adquisiciones de empresas.
Diseñar para el cambio no significa sobre-ingeniería especulativa. Significa adoptar patrones que faciliten modificaciones futuras sin tiempos de inactividad prolongados o migraciones masivas de datos.
Patrones para flexibilidad controlada
Tablas de extensión (EAV con límites):
El patrón Entity-Attribute-Value permite una flexibilidad extrema: cada atributo es una fila en una tabla de atributos. Esto suena atractivo para dominios con alta variabilidad (catálogos de productos diversos, formularios personalizables..).
El problema es que el EAV puro es una pesadilla de consulta y rendimiento. Un compromiso práctico: mantener atributos comunes y críticos como columnas tradicionales, y usar tablas de extensión solo para atributos opcionales o específicos de subtipos.
Columnas JSON/JSONB:
PostgreSQL, MySQL y otros motores modernos soportan tipos JSON con indexación y consultas. Esto permite flexibilidad estructurada: puedes añadir campos sin migraciones de esquema, pero aún así consultar y indexar eficientemente.
La tentación es abusar de JSON como "cajón de sastre". Hay que resistirse a eso. JSON funciona bien para datos semi-estructurados que raramente se consultan directamente, o para atributos específicos de subtipo. Para datos críticos de negocio con consultas frecuentes, las columnas tradicionales siguen siendo superiores.
Versionado de esquemas:
En lugar de modificar esquemas existentes, algunos equipos crean nuevas versiones. Esto permite migraciones graduales y rollbacks sencillos, pero introduce complejidad: múltiples esquemas coexisten, las consultas deben unificar versiones, y eventualmente necesitas consolidar.
El versionado funciona mejor con estrategias de despliegue blue-green y cuando tienes confianza en tu proceso de migración de datos.
Migraciones sin tiempo de inactividad
Las migraciones tradicionales ("apagar sistema, modificar esquema, reiniciar") son inaceptables para servicios modernos que prometen disponibilidad continua. Las migraciones sin downtime requieren planificación pero son factibles.
Patrón típico para añadir una columna:
- Añadir la nueva columna como nullable
- Desplegar el código que escribe en la columna vieja y la nueva
- Migrar los datos históricos en background (lotes pequeños)
- Verificar la coherencia de datos
- Desplegar el código que solo lee columna nueva
- Eliminar la columna vieja
Este proceso puede llevar días o semanas para tablas masivas, pero permite rollback en cada etapa y zero downtime.
Herramientas que facilitan el proceso:
- gh-ost (GitHub): Replica tabla, aplica cambios a réplica, sincroniza cambios online, y hace switchover atómico
- pt-online-schema-change (Percona): Similar a gh-ost, pero para MySQL
- Rails migrations y Django migrations: Frameworks que manejan versionado de esquema en la aplicación
Antipatrones y errores comunes
El mega-join del terror
Esto se ve repetidamente: consultas que joinean 15-20 tablas para producir un informe. Cada join introduce overhead computacional y riesgo de productos cartesianos accidentales. Si tus consultas críticas requieren tantos joins, tu modelo está sobre-normalizado para esa carga de trabajo.
Solución: Vistas materializadas o tablas agregadas para consultas frecuentes. Acepta la desnormalización controlada en la capa de consumo.
El identificador natural que resultó no ser único
Elegir identificadores de negocio (email, número de empleado) como claves primarias parece intuitivo. El problema surge cuando descubres que tu asunción de unicidad era incorrecta, o que el identificador necesita cambiar.
Solución: Siempre usar claves sintéticas (auto-incrementos, UUIDs) como primarias. Los identificadores de negocio pueden ser unique indexes, pero no claves primarias.
La trampa del "podríamos necesitarlo"
Diseñar campos o tablas anticipando requisitos especulativos genera esquemas inflados y confusos. Cada columna no utilizada es documentación obsoleta y fuente potencial de confusión.
Solución: Diseñar para requisitos actuales y conocidos. Confiar en que tus patrones de esquema evolutivo manejarán futuros cambios.
Ignorar las constraints y foreign keys
En un intento de maximizar rendimiento o flexibilidad, algunos equipos omiten los constraints de integridad referencial. Esto transfiere la responsabilidad de coherencia a la aplicación, donde es más fácil cometer errores.
Solución: Usar constraints a nivel de base de datos para reglas de negocio invariantes. Si el rendimiento es crítico, considera usar constraints sin enforcement inmediato (deferred constraints) o validación asíncrona para casos específicos.
Checklist operativo: validaciones antes de aprobar un modelo
En las organizaciones maduras, ningún modelo de datos llega a producción sin haber pasado por un conjunto estricto de validaciones técnicas y de negocio. Este checklist no es burocracia: es una defensa activa contra la deuda técnica, los costes inesperados y futuros cuellos de botella. A continuación se presenta una versión práctica —al estilo de una arquitectura real en empresas con fuerte disciplina de datos— que cualquier CIO o arquitecto debería aplicar.
1. ¿El modelo está alineado con un caso de uso concreto y no con una abstracción genérica?
La pregunta más importante.
Un modelo sin dueño real siempre termina fracasando.
-
¿Qué dominio lo utiliza?
-
¿Qué microservicio, proceso batch o dashboard depende de él?
-
¿Qué métricas de negocio lo justifican?
Si el modelo no tiene un propietario claro (data product owner), no está listo.
2. ¿Está diseñado según el tipo de carga dominante (OLTP/OLAP/stream/batch)?
El error típico: un modelo OLTP “prestado” para analítica, o una tabla analítica utilizada como fuente para APIs en real time.
Validaciones:
-
¿El modelo OLTP está normalizado?
-
¿El modelo OLAP está desnormalizado o materializado correctamente?
-
¿El modelo en streaming maneja idempotencia, orden y late data?
-
¿El modelo batch tiene cortes diarios claros y es determinístico?
Si el modelo no encaja en su cuadrante, generará problemas a largo plazo.
3. ¿El modelo respeta la clave de partición y evita joins distribuidos?
Especialmente crítico en:
-
BigQuery
-
Snowflake
-
Redshift
-
Databricks
-
Spanner
-
CockroachDB
-
Yugabyte
-
TiDB
Preguntas clave:
-
¿Qué columna define el particionado natural?
-
¿Las tablas asociadas comparten la misma clave para evitar shuffles?
-
¿Los accesos más frecuentes están optimizados para consultas locales?
Si un join habitual requiere movimiento de datos entre nodos, el modelo no es válido.
4. ¿Existen rutas de acceso optimizadas (modelos por acceso) para las consultas críticas?
Cualquier modelo real tiene puntos calientes.
Los accesos de altísima frecuencia deben tener su propio modelo derivado.
Checklist:
-
¿Las consultas críticas se resuelven sin joins?
-
¿Hay materializaciones, proyecciones o tablas wide para los dashboards más usados?
-
¿El modelo por acceso está versionado y documentado?
-
¿Existe un mecanismo para refrescarlo sin afectar al sistema origen?
Si el equipo espera que un modelo normalizado responda a cargas intensas, el diseño fallará.
5. ¿El modelo incorpora control de cambios, versionado y backward compatibility?
Preguntas clave:
-
¿El esquema puede evolucionar sin dejar sin servicio a los consumidores?
-
¿Las columnas nuevas son null-safe?
-
¿Los renombrados están gestionados vía vistas transitorias o flags de compatibilidad?
-
¿Se usa schema registry (Avro/JSON/Protobuf) en pipelines asíncronos?
Un modelo sin estrategia de evolución es un accidente en cámara lenta.
6. ¿El coste del modelo está estimado antes de aprobarlo?
Una de las causas más frecuentes de facturas inesperadas.
Validaciones:
-
¿Cuánto cuesta escanear estas tablas en BigQuery/Snowflake?
-
¿Cuántas veces al día se consultará este modelo?
-
¿Los usuarios pueden hacer full scans sin restricciones?
-
¿Hay partición por fecha para limitar escaneos masivos?
Si no existe estimación de coste, el modelo no debería aprobarse.
7. ¿El modelo está auditado para privacidad, cumplimiento y retención?
Crítico en sectores regulados:
-
¿Las columnas sensibles están enmascaradas o cifradas?
-
¿Se cumple GDPR (minimización, retención, finalidad)?
-
¿Existe una política de borrado automatizado?
-
¿El lineage identifica claramente el origen del dato sensible?
Sin garantías de cumplimiento, el modelo es inaceptable.
El checklist es el guardarraíl que evita la deuda técnica masiva
A escala, los errores de modelado no son simplemente “incómodos”:
son multiplicadores de coste, riesgo y complejidad.
Este checklist actúa como contrato de calidad entre negocio, ingeniería y arquitectura. Si el modelo lo supera, probablemente está listo para vivir en producción.
Checklist operativo técnico por fases
A nivel de más detalle técnico esta propuesta de checklist también puede resultar muy útil
Fase de diseño inicial
- Identificar top 10 consultas críticas por frecuencia y requisitos de latencia
- Documentar ratio lectura/escritura para cada entidad principal
- Definir requisitos de consistencia: ¿Eventual está bien o necesitamos strong consistency?
- Establecer proyección de crecimiento: volúmenes de datos a 1, 3, y 5 años
- Seleccionar estrategia de particionado para tablas que superarán 100M filas
- Diseñar dimensiones conformadas para entidades compartidas entre dominios
- Validar tipos de datos: tamaños apropiados, no sobre-provisioning
Implementación
- Implementar claves primarias sintéticas para todas las entidades
- Crear índices para patrones de acceso críticos (top 5 consultas)
- Configurar foreign keys con ON DELETE/UPDATE apropiado
- Implementar constraints de negocio (checks, unique) a nivel de BD
- Documentar decisiones de desnormalización y estrategia de sincronización
- Establecer naming conventions consistentes para tablas, columnas, índices
- Crear scripts de migración versionados y reversibles
Validación y monitorización
- Ejecutar load testing con datos de volumen realista (10x producción esperada)
- Medir latencia de consultas críticas bajo diferentes cargas
- Verificar distribución en particiones: desviación <20% entre particiones
- Monitorizar índice hit ratio: objetivo >95% para índices críticos
- Configurar alertas en query performance degradation
- Establecer proceso de review para nuevos índices (evitar proliferación)
- Documentar query patterns para análisis futuro de optimización
Evolución continua
- Review trimestral de query patterns: ¿han cambiado los accesos críticos?
- Identificar índices no utilizados (candidates para eliminación)
- Evaluar particiones para archivado: datos >2 años raramente accedidos
- Planificar migraciones de esquema con mínimo 2 semanas anticipación
- Mantener changelog de esquema vinculado a releases de aplicación
- Ejecutar DR drills incluyendo restore de esquema y datos
Caso práctico 1: el rediseño del modelo de datos de un marketplace y su impacto real
Para entender cómo los principios de normalización, desnormalización y modelos por acceso se aplican en el mundo real, analizaremos un caso práctico inspirado en un marketplace europeo de tamaño medio, especializado en productos de hogar y electrónica, con más de 1.500 vendedores, 5 millones de SKUs y picos diarios de 500.000 transacciones.
La empresa crecía a buen ritmo, pero el equipo de datos sufría tres problemas que ya empezaban a notarse en la experiencia de usuario, en las métricas de ventas y en la factura del cloud:
-
Latencias elevadas en consultas transaccionales (como actualizar un carrito o mostrar la disponibilidad local).
-
Dashboards analíticos lentos y caros, con picos de facturación inesperados.
-
Inconsistencias entre fuentes: lo que un microservicio llamaba “producto disponible” no coincidía con lo que veían los vendedores en su panel.
La raíz del problema era evidente para cualquier arquitecto experimentado: un modelo único y normalizado que intentaba servir simultáneamente a OLTP, OLAP, reporting y APIs internas. Una receta clásica para el desastre cuando la empresa supera cierto volumen.
El modelo original: normalización impecable, rendimiento desastroso
El equipo de ingeniería había creado un modelo relacional tradicional con siete tablas principales:
-
products
-
sellers
-
stock_items
-
prices
-
categories
-
catalog_attributes
-
transactions
Técnicamente era elegante: alta normalización, claves foráneas, integridad impecable.
Pero operativamente era una trampa:
-
La consulta para renderizar un producto requería 6 joins.
-
Los joins entre products y stock_items eran cross-region, porque la clave de partición era el product_id, mientras que los vendedores gestionaban stock por almacén.
-
Los dashboards diarios en BigQuery escaneaban cientos de GB, ya que la normalización obligaba a joins masivos para calcular métricas simples.
-
La tabla de transacciones generaba hot partitions, porque la partición se hacía por fecha de creación y el 80% de operaciones ocurrían en franjas de dos horas.
Todo ello se traducía en:
-
latencias erráticas,
-
picos de CPU en el motor OLTP,
-
dashboards que tardaban minutos,
-
costes cloud que crecían un 40% trimestral.
La decisión: dividir el modelo en tres representaciones independientes
Tras un análisis completo, el equipo de arquitectura definió una estrategia basada en tres modelos complementarios, cada uno diseñado para su cuadrante OLTP/OLAP/stream:
1. Un modelo OLTP normalizado (pero particionado correctamente)
Se mantuvo la normalización… pero rediseñando la clave de partición:
-
stock_items pasó a particionarse por (seller_id, warehouse_id),
-
products mantuvo partición por (product_id),
-
prices cambió a partición por (product_id, marketplace_id),
-
se eliminaron joins entre dominios críticos usando proyecciones ligeras (por ejemplo, duplicar el nombre del producto en la tabla stock_items para consultas rápidas).
Resultado:
-
consultas de disponibilidad un 65% más rápidas,
-
latencias más estables durante picos nocturnos,
-
carga distribuida uniformemente entre nodos.
2. Un modelo analítico completamente desnormalizado
La empresa adoptó un enfoque estilo lakehouse:
-
tablas product_wide,
-
tablas transactions_daily,
-
modelos de ventas por categoría ya preagregados,
-
vistas materializadas para métricas diarias.
Se eliminaron más de 12 joins comunes que antes eran obligatorias, y se adoptaron:
-
partición por fecha,
-
clustering por categoría y seller_id,
-
eliminación de columnas de baja utilidad.
Resultado:
-
dashboards del comité ejecutivo: de 90 segundos a 7 segundos,
-
coste mensual del warehouse: -32%,
-
consultas ad-hoc: mucho más rápidas y estables.
3. Un modelo por acceso para APIs y microservicios
Aquí llegó la mayor transformación conceptual:
cada servicio crítico recibió su propio modelo especializado, construido mediante un pipeline de eventos.
Ejemplos:
-
product_snapshot_by_region para la API pública, agrupando stock geolocalizado.
-
seller_performance_profile para el panel del vendedor.
-
cart_optimized_prices para el microservicio de checkout.
Los pipelines usaban:
-
CDC para datos estructurales,
-
Kafka para eventos de marketplace,
-
snapshots diarios para reconstrucción completa,
-
transformaciones ELT en el warehouse.
Resultado:
-
la API pública redujo un 80% la latencia promedio,
-
checkout pasó de 300ms a 90ms,
-
se eliminaron rutas de acceso “maliciosas” que antes forzaban joins absurdamente caros.
El mayor beneficio: coherencia entre equipos y fin de la “guerra de métricas”
Antes del rediseño, cada equipo tenía su propia interpretación de:
-
qué era un producto activo,
-
cómo se calculaba stock disponible,
-
qué transacciones contaban como válidas,
-
cómo se computaban devoluciones.
Con la separación de modelos:
-
El modelo OLTP generaba la verdad transaccional.
-
El modelo analítico calculaba métricas coherentes y auditables.
-
Los modelos por acceso empaquetaban los datos según necesidades de cada servicio.
Por primera vez, el marketplace podía asegurar que:
-
las métricas de ventas del BI coincidían con las del panel de vendedor,
-
el checkout mostraba stock real,
-
la comparativa de precios no dependía de una consulta compleja sino de un modelo precompuesto.
Esto generó no solo mejoras técnicas, sino culturales:
-
menos fricción entre equipos,
-
menos debates interminables sobre definiciones,
-
menos hotfixes improvisados,
-
más velocidad para lanzar funcionalidades.
Métricas finales del proyecto (6 meses después)
| Métrica | Antes | Después | Mejora |
|---|---|---|---|
| Latencia checkout | 300–350 ms | 90–120 ms | -65% |
| Tiempo de dashboards | 60–120 s | 5–12 s | -85% |
| Coste mensual del warehouse | — | — | -32% |
| Picos de CPU OLTP | 80–90% | 40–55% | -45% |
| Incidencias por inconsistencia | decenas/mes | <3/mes | -80% |
| Tiempo para lanzar nuevas features | 3–6 semanas | 1–3 semanas | -50% |
Lecciones clave del caso
-
Un modelo único nunca sirve para todos los usos.
Lo que es óptimo para OLTP es pésimo para analítica. -
La partición manda.
El mejor modelo normalizado puede romperse si ignora la distribución. -
Los modelos por acceso son obligatorios en empresas con microservicios.
-
Desnormalizar no es una decisión estética, es una decisión económica.
-
La coherencia de métricas no viene del modelo, viene de la gobernanza del modelo.
Caso práctico 2: Rediseño de esquema para plataforma de streaming
Contexto
Una plataforma de streaming musical con 50M usuarios enfrentaba degradación severa de rendimiento. Las consultas a la página de inicio (feed personalizado) tardaban 2-4 segundos, muy por encima del objetivo de 300ms. El modelo de datos original era altamente normalizado, optimizado para integridad transaccional pero no para lecturas masivas.
El modelo original
La consulta del feed personalizado requería:
- 6-8 joins para obtener canciones con metadata completa
- Agregación de plays recientes para cálculo de popularidad
- Filtrado por follows del usuario para contenido relevante
- Ordenamiento complejo por múltiples criterios
Con 50M usuarios y 500M canciones, la tabla user_plays contenía 100B registros y crecía 50M diarios.
Análisis de patrones de acceso
El equipo instrumentó queries durante 2 semanas, revelando:
- Feed de inicio: 5M requests/día, 95th percentile 2.3s
- Reproducción de canción: 200M requests/día, p95 45ms (ya optimizado)
- Búsqueda: 10M requests/día, p95 800ms
- Analytics para artistas: 50K requests/día, p95 12s (aceptable para uso interno)
El 90% del tiempo de ingeniería de bases de datos se gastaba optimizando el feed, que representaba solo 2.4% de las consultas pero era crítico para experiencia de usuario.
Rediseño por patrones de acceso
Decisión 1: Tabla desnormalizada para feed
Crearon user_feed_cache con datos pre-computados:
Esto eliminaba 6 joins en tiempo de consulta. La tabla se regeneraba cada 6 horas mediante job batch, usando ventana de 72h de plays para cálculo de relevancia.
Decisión 2: Particionado agresivo de user_plays
Particionaron user_plays por mes (pg_partman en PostgreSQL). Esto permitía:
- Queries analíticas operan solo en particiones relevantes
- Archivado automático de particiones >12 meses a cold storage (S3)
- Reducción de 100B filas activas a 12B (88% menor)
Decisión 3: Índices compuestos específicos
Crearon índice covering en user_plays:
sql
Actualizada cada hora para artistas activos, diariamente para el resto.
Implementación gradual
El rollout tomó 8 semanas:
Semanas 1-2: Infraestructura
- Configurar particionado en réplica read-only
- Validar proceso de archivado en staging
- Implementar jobs de pre-computación de feed
Semanas 3-4: Despliegue inicial
- Activar feed pre-computado para 1% de usuarios
- Monitorizar latencias, consistencia de datos
- Ajustar frecuencia de regeneración basado en freshness requirements
Semanas 5-6: Escalado
- Incrementar a 10%, luego 50% de usuarios
- Identificar y corregir edge cases (usuarios sin datos recientes)
- Optimizar jobs de pre-computación (reducir de 4h a 45min)
Semanas 7-8: Migración completa
- Activar para 100% usuarios
- Archivar código legacy de consultas join-heavy
- Documentar nuevo modelo y playbooks operativos
Resultados
Rendimiento:
- Feed latency p95: 2.3s → 180ms (92% mejora)
- Feed latency p99: 4.1s → 320ms (92% mejora)
- Carga CPU en cluster de BD: -65%
- Consultas bloqueantes eliminadas completamente
Costes:
- Storage activo: -72% (gracias a archivado)
- Cómputo mensual: +15% (jobs de pre-computación)
- Ahorro neto: 48% en costes de BD
Trade-offs aceptados:
- Freshness del feed: Inmediato → 6h lag (usuarios no notaron diferencia en tests A/B)
- Complejidad operativa: +2 jobs críticos adicionales para monitorizar
- Tiempo de incorporación de nuevos patrones: más lento (requiere evaluar si necesitan pre-computación)
Lecciones aprendidas
- La normalización académica no es el objetivo: Optimizar para el caso de uso, no para el libro de texto.
- Medir antes de asumir: Instrumentación reveló que 90% del tiempo se gastaba en 2% de las queries. Optimizar ese 2% generó 10x mejor ROI que optimizaciones generales.
- El "tiempo real" es un espectro: Los usuarios aceptaron 6h de lag cuando la experiencia se volvió instantánea. No todo necesita consistencia inmediata.
- Pre-computar es válido: Con almacenamiento barato, pre-computar vistas costosas y actualizarlas periódicamente es una estrategia legítima, no una chapuza.
- El cambio debe ser gradual: El rollout conservador (1% → 10% → 50% → 100%) permitió detectar problemas con impacto limitado. En la primera fase descubrieron que usuarios sin plays recientes rompían el feed.
Recursos y lecturas recomendadas
Un capítulo sobre diseño de esquemas y modelos para escalar nunca está definitivamente cerrado: la evolución constante de los motores de bases de datos, los patrones de arquitectura distribuida y la presión de la analítica real-time obligan a actualizar conocimientos continuamente. A continuación se incluye una selección de recursos que complementan los conceptos tratados y ayudan a ampliar matices tanto teóricos como prácticos.
Hemos seleccionado materiales con criterios de calidad: profundidad técnica, autoridad en la materia y aplicabilidad directa al trabajo de un arquitecto o ingeniero de datos moderno.
Libros esenciales
1. “Designing Data-Intensive Applications” — Martin Kleppmann
La referencia definitiva para entender consistencia, replicación, particionado y arquitectura distribuida. El libro no dicta cómo normalizar o desnormalizar, sino cuándo, por qué y qué consecuencias tiene.
2. “Database Internals” — Alex Petrov
Una inmersión profunda en cómo funcionan realmente los motores de bases de datos: B-Trees, LSM trees, transacciones distribuidas, replicación, compaction. Fundamental para entender por qué un modelo performa bien o mal en distintos escenarios.
3. “The Data Warehouse Toolkit” — Ralph Kimball & Margy Ross
El libro clásico sobre modelado dimensional. Aunque su enfoque está orientado a OLAP tradicional, sigue siendo tremendamente útil para comprender desnormalización analítica, hechos, dimensiones, granularidades y tablas wide efectivas.
4. “Stream Processing with Apache Kafka” — Neha Narkhede, Gwen Shapira y Todd Palino
No es solo un libro sobre Kafka: es una guía práctica de cómo modelar estados, eventos y proyecciones bajo un paradigma de streaming.
Recursos técnicos avanzados (whitepapers / blogs de ingeniería)
Snowflake Engineering Blog
Imprescindible para comprender cómo funcionan el clustering, el micro-particionado, las vistas materializadas y el impacto real del escaneo.
Google Spanner Whitepapers
Especialmente relevantes los documentos sobre TrueTime, particionado, transacciones distribuidas y replicación sincrónica global.
Databricks Lakehouse Paper
Una descripción moderna del enfoque lakehouse, optimización de tablas de gran tamaño (Delta Lake), Z-ordering y estrategias de almacenamiento.
Herramientas y frameworks útiles para modelar, validar y operar esquemas
dbt (Data Build Tool)
La mejor herramienta moderna para construir modelos analíticos reproducibles, documentados y testeados. Permite gestionar modelos wide, vistas materializadas y contratos.
Schema Registry (Avro / Protobuf)
Obligatorio para cualquier pipeline basado en eventos. Garantiza evolución segura de esquemas, backward compatibility y gobernanza real.
Amundsen / DataHub / Collibra / Atlan
Catálogos modernos que facilitan lineage, control de metadatos y documentación viva de modelos.
Apache Iceberg / Delta Lake / Hudi
Para arquitecturas lakehouse, son la base para modelos OLAP estructurados, particionados y versionados.
pgTAP (PostgreSQL)
Framework de testing para constraints, functions, datos
Great Expectations
Validación de calidad de datos, útil post-migración para verificar consistencia
Herramientas de diseño y documentación
- dbdiagram.io / DrawSQL: Diseño visual de esquemas, generación de SQL, colaboración en equipo
- ERD en DataGrip / DBeaver: IDEs de BD con visualización automática de relaciones
- SchemaSpy: Documentación automática de esquemas con diagramas ER interactivos
- erwin Data Modeler: Completo pero costoso, útil si necesitas gobernanza estricta
- ER/Studio: Similar a erwin, se integra con herramientas de metadata enterprise
Gestión de migraciones
- Flyway / Liquibase: Versionado de esquemas, aplicación ordenada de migraciones, rollback
- Alembic (Python): Integrado con SQLAlchemy, excelente para proyectos Python/data
- Atlas (Ariga): Moderno, soporta esquemas declarativos y aplicación automática de diffs
- gh-ost (GitHub): Para MySQL/MariaDB, permite alter table sin bloqueos
- pgroll (Xata): Similar para PostgreSQL, aún inmaduro pero prometedor
Conclusión
El diseño de esquemas de datos no es un ejercicio académico único al inicio del proyecto. Es una disciplina continua que equilibra teoría con pragmatismo, pureza con rendimiento, consistencia con escala.
Los mejores diseños de esquemas no son los más normalizados ni los más desnormalizados. Son aquellos que comprenden profundamente sus patrones de acceso, optimizan agresivamente los casos críticos, y mantienen flexibilidad para evolucionar con el negocio.
En la próxima década, veremos convergencia entre modelos relacionales y documentales, con motores que soportan nativamente esquemas híbridos. Pero los principios fundamentales - entender tus datos, conocer tus accesos, medir antes de optimizar - permanecerán constantes.
El esquema que diseñes hoy vivirá más tiempo que la mayoría de tu código. Invierte el tiempo necesario en hacerlo bien.
