Logo GCP con Eduardo

GCP con Eduardo

Descarga el código de la lección

Estructura de Modelos en dbt: Marts, Capa Dimensional y Materializaciones (View vs Table)
✦ Guía Técnica & Arquitectura

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.

Arquitectura de datos

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.

Stack

Tecnologías y conceptos

dbt SQL Jinja ref() Models Marts Fact Tables Dimensions View Table Incremental DAG Data Warehouse
Matriz de decisión

¿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 Mayor durante el build Snapshot lógico del resultado materializado Agregaciones pesadas, joins complejos, marts de consumo frecuente y modelos costosos.
Incremental 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.

FinOps & Production

Antipatrones que terminan en una factura innecesaria

Antipatrón crítico: convertir todo en views

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.
Segundo antipatrón: SELECT * en marts

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.

Implementación práctica

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.

models/marts/sales/fct_orders.sql dbt + SQL + Jinja
{{ 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.

Design Patterns

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.

01

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.

02

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.

03

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.

04

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.

05

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.

06

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.

Modelado dimensional

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
Framework de implementación

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.

FAQ

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.

Perfil profesional

Sobre el Autor: Eduardo Martínez Agrelo

AI & Data Architect

Eduardo Martínez Agrelo es AI & Data Architect, especializado en arquitectura de datos, ingeniería de datos y diseño de plataformas analíticas. Su enfoque combina profundidad técnica con criterios de arquitectura empresarial: escalabilidad, gobernanza, rendimiento, mantenibilidad y control del coste.

En este tipo de contenidos, el objetivo es ir más allá del ejemplo que simplemente funciona y analizar por qué una decisión de diseño puede ser adecuada —o convertirse en deuda técnica— cuando el sistema crece.

© 2026 Eduardo Martínez Agrelo · AI & Data Architect · Arquitectura de Datos y Analytics Engineering