Estructura de Modelos en dbt: Marts, Capa Dimensional y Materializaciones (View vs Table)
Diseñar modelos en dbt no consiste únicamente en escribir SQL limpio. A nivel Senior, Staff o Architect, la decisión crítica está en definir dónde vive cada transformación, qué contrato ofrece cada modelo, cómo se construye el DAG y cuándo una relación debe materializarse como view, table o incremental. Esta guía presenta una arquitectura orientada a producción para construir una capa marts dbt mantenible, observable y eficiente en costes.
Lo que aprenderás
El objetivo no es memorizar configuraciones de dbt, sino entender los criterios arquitectónicos que determinan si un proyecto escalará correctamente cuando aumenten los datos, los consumidores y la frecuencia de ejecución.
Diseño por capas
Diferenciar staging, intermediate y marts para evitar mezclar ingestión, lógica técnica y semántica de negocio.
Modelado dimensional
Diseñar fact tables y dimensiones con granularidad, claves y contratos explícitos.
View vs Table
Elegir la materialización según coste computacional, frecuencia de lectura, volumen y SLA.
DAG y ref()
Utilizar ref() para expresar dependencias y construir pipelines reproducibles entre entornos.
Tecnologías y conceptos
¿View, Table o Incremental?
La materialización no debería decidirse por preferencia personal. Es una decisión de arquitectura condicionada por el coste de transformación, el patrón de consumo y la capacidad del warehouse.
| Patrón | Latencia de consulta | Coste de construcción | Consistencia | Cuándo utilizarlo |
|---|---|---|---|---|
| View | Depende de la complejidad SQL al consultar | Bajo durante el build | Datos calculados sobre la relación subyacente | Transformaciones ligeras, modelos intermedios y lógica cuya ejecución repetida sea asumible. |
| Table | Generalmente menor para lecturas repetitivas | Mayor durante el build | Snapshot lógico del resultado materializado | Agregaciones pesadas, joins complejos, marts de consumo frecuente y modelos costosos. |
| Incremental | Optimizada para grandes volúmenes persistentes | Reducido frente a reconstruir todo el dataset | Depende de la estrategia incremental y de la lógica de actualización | Fact tables grandes, eventos, históricos y cargas donde procesar únicamente nuevos o modificados registros reduzca significativamente el coste. |
Regla arquitectónica
Una view no es automáticamente más eficiente por no almacenar datos, igual que una table no es automáticamente mejor por precalcularlos. El criterio correcto es desplazar el coste hacia el punto donde resulte más barato: durante el build, durante la consulta o de forma incremental.
Antipatrones que terminan en una factura innecesaria
Un error habitual consiste en interpretar la materialización view como
una estrategia de ahorro universal. Una view puede tener un coste de almacenamiento
prácticamente nulo, pero eso no significa que su consulta sea gratuita.
Imagina una cadena donde un dashboard consulta una view de ventas que realiza varios joins, normalizaciones y agregaciones sobre millones o miles de millones de registros. Si decenas de usuarios ejecutan ese dashboard simultáneamente, el mismo trabajo computacional puede repetirse una y otra vez.
- Problema: se desplaza todo el coste al momento de lectura.
- Síntoma: consultas lentas, picos de consumo y costes variables.
- Solución: materializar como table cuando el resultado sea estable y reutilizado frecuentemente.
- Para históricos grandes: evaluar una estrategia incremental.
Un mart es una interfaz de datos, no un espejo del origen. Utilizar
SELECT * en modelos finales acopla el contrato de consumo al esquema
upstream y facilita que cambios aparentemente inocentes propaguen columnas,
costes y ambigüedades a todo el ecosistema.
En una capa de consumo, seleccionar explícitamente las columnas permite controlar el contrato, documentar la semántica y reducir el volumen de datos procesados.
Un mart de ventas diseñado para producción
La siguiente implementación muestra el patrón de separación entre una fuente preparada, dimensiones y una fact table. La clave arquitectónica está en que el modelo de consumo no dependa de nombres físicos hardcodeados.
{{ config(
materialized = 'table',
tags = ['mart', 'sales']
) }}
WITH orders AS (
SELECT
order_id,
customer_id,
order_date,
status,
currency,
gross_amount,
discount_amount,
tax_amount
FROM {{ ref('stg_orders') }}
),
customers AS (
SELECT
customer_id,
customer_key
FROM {{ ref('dim_customers') }}
),
final AS (
SELECT
o.order_id,
c.customer_key,
o.order_date,
o.status,
o.currency,
o.gross_amount,
o.discount_amount,
o.tax_amount,
o.gross_amount - o.discount_amount AS net_amount
FROM orders AS o
INNER JOIN customers AS c
ON o.customer_id = c.customer_id
)
SELECT *
FROM final
¿Qué hace realmente ref()?
La función ref() no es simplemente una forma más elegante de escribir
el nombre de una tabla. Expresa una dependencia lógica entre modelos. dbt puede
utilizar esa relación para construir el DAG, determinar el orden de ejecución y
resolver el nombre de la relación según el target y el entorno.
Por eso, en una arquitectura dbt seria, las dependencias entre modelos deben
expresarse mediante ref() siempre que se esté referenciando otro modelo
gestionado por dbt.
Patrones de diseño para una capa marts escalable
El objetivo de la arquitectura no es crear más carpetas, sino establecer límites claros entre responsabilidades y reducir el acoplamiento entre productores y consumidores.
Staging como frontera técnica
La capa staging debe resolver problemas de nomenclatura, tipos, casts, normalización básica y selección controlada de campos. No debería convertirse en el lugar donde se concentra toda la lógica de negocio.
Intermediate para composición
Cuando una transformación es demasiado compleja para staging pero todavía no representa una entidad final de negocio, una capa intermediate permite dividir el problema y reutilizar lógica sin contaminar los marts.
Marts como contrato
Un mart debe estar diseñado para consumidores concretos: BI, analistas, aplicaciones analíticas o productos de datos. Sus columnas, granularidad y semántica deben ser deliberadas y estables.
Dimensiones conformadas
Si diferentes procesos necesitan la misma definición de cliente, producto, calendario u otra entidad, una dimensión compartida puede actuar como contrato semántico común.
Granularidad explícita
Una fact table debe poder responder una pregunta básica: ¿qué representa exactamente una fila? Sin una granularidad inequívoca aparecen duplicados, joins incorrectos y métricas infladas.
Materialización basada en carga
La materialización debe seguir el patrón de acceso. Modelos pequeños y ligeros pueden ser views; resultados pesados y reutilizados suelen justificar tables; datasets masivos con histórico creciente pueden requerir incremental.
Fact tables y dimensiones: el contrato importa más que el SQL
El error más caro de un modelo dimensional no suele ser una sintaxis SQL incorrecta, sino una granularidad mal definida. Antes de escribir una consulta debemos poder explicar qué representa una fila de la fact table.
| Componente | Responsabilidad | Pregunta arquitectónica | Riesgo habitual |
|---|---|---|---|
| Fact table | Eventos o hechos cuantificables del negocio | ¿Qué representa una fila? | Doble conteo por joins con granularidad incompatible |
| Dimension | Contexto descriptivo de los hechos | ¿Cuál es la clave de negocio? | Duplicidad de entidades o cambios históricos mal tratados |
| Mart | Interfaz analítica orientada a un dominio | ¿Quién consume este modelo? | Convertirse en un modelo genérico sin contrato |
| Intermediate | Composición y transformación reutilizable | ¿Esta lógica es de negocio o de preparación? | Acumular lógica que nadie entiende ni reutiliza |
Checklist para implementar una arquitectura dbt empresarial
Utiliza este flujo como mecanismo de revisión antes de convertir un conjunto de consultas SQL en una arquitectura de modelos mantenible.
Define el dominio y los consumidores
Identifica si el modelo pertenece a ventas, marketing, finanzas, producto u otro dominio y quién utilizará su contrato.
Define la granularidad
Especifica qué representa una fila antes de añadir métricas, dimensiones o joins.
Separa staging de negocio
Mantén la preparación técnica separada de las decisiones semánticas y reglas de negocio.
Construye dependencias con ref()
Evita nombres de tablas hardcodeados cuando la relación corresponda a otro modelo gestionado por dbt.
Elige la materialización por patrón de acceso
Evalúa volumen, frecuencia de consulta, complejidad, latencia requerida y coste de recomputación antes de elegir view o table.
Evalúa incremental en históricos grandes
Si el dataset crece continuamente, determina si es posible procesar solamente registros nuevos o modificados de forma segura.
Valida claves y relaciones
Añade tests para unicidad, valores nulos y relaciones entre dimensiones y facts donde sean relevantes.
Observa rendimiento y coste
Una arquitectura correcta debe medirse con tiempos de ejecución, volumen procesado, frecuencia de builds y coste por dominio o pipeline.
Preguntas frecuentes sobre modelos en dbt
¿Cuándo debo utilizar una materialización view frente a table en dbt?
Una view es apropiada para transformaciones ligeras o reutilizables cuyo coste de ejecución sea aceptable en cada consulta. Una table suele ser preferible para modelos pesados, joins complejos, agregaciones costosas o datasets consultados con mucha frecuencia. Para históricos de gran tamaño, también conviene evaluar incremental.
¿Por qué es importante utilizar ref() en los modelos dbt?
ref() crea una dependencia explícita entre modelos. Esto permite que dbt construya el DAG, determine el orden de ejecución y resuelva correctamente las relaciones según el entorno y la configuración del proyecto. Además, hace que la arquitectura sea más portable y mantenible que utilizar nombres físicos hardcodeados.
¿Cómo debería estructurarse una capa marts en dbt?
Una capa marts dbt debería exponer modelos orientados al consumo analítico y organizados por dominio de negocio. Cuando el caso lo requiera, puede utilizar patrones dimensionales como fact tables y dimensiones conformadas. El criterio fundamental es que cada mart tenga una granularidad, semántica y contrato de consumo claros.
