Tutorial dbt completo con DuckDB y Docker: pipeline, tests y docs

Resource type
Demo
Tutorial dbt con DuckDB y Docker - Construye un pipeline de datos profesional con modelos SQL, tests y documentación autogenerada

En el ecosistema moderno de datos, la transformación ya no es un paso oscuro enterrado dentro de un ETL monolítico. Hoy, los equipos de analítica necesitan pipelines reproducibles, testeables y documentados. Ahí es exactamente donde entra dbt (data build tool): la herramienta que ha revolucionado cómo los analistas e ingenieros de datos transforman información dentro del data warehouse.

En este tutorial construiremos, paso a paso, un proyecto dbt completo usando DuckDB como base de datos analítica (sin necesidad de servidor) y Docker para garantizar un entorno reproducible. Al final tendrás un pipeline funcional con modelos en tres capas, tests de calidad de datos y documentación autogenerada con lineage graph.

Contenido del artículo

  1. Qué es dbt y por qué debería importarte
  2. Arquitectura del proyecto: staging, intermediate y marts
  3. Instalación y configuración del entorno
  4. Tu primer proyecto dbt desde cero
  5. Capa 1: Modelos de staging
  6. Capa 2: Modelos intermediate
  7. Capa 3: Modelos marts (hechos y dimensiones)
  8. Tests de calidad de datos
  9. Documentación y lineage graph
  10. Despliegue con Docker
  11. Conclusión y próximos pasos

1. Qué es dbt y por qué debería importarte

dbt (data build tool) es un framework open source que permite transformar datos dentro de un data warehouse utilizando únicamente SQL. A diferencia de las herramientas ETL tradicionales que requieren interfaces gráficas complejas o lenguajes propietarios, dbt aplica las mejores prácticas de la ingeniería de software al mundo de los datos:

  • Versionado con Git: cada transformación es un archivo SQL que vive en un repositorio, con historial completo de cambios.
  • Modularidad: los modelos se referencian entre sí con la función {{ ref() }}, creando un grafo de dependencias automático.
  • Testing integrado: puedes definir tests de calidad (unicidad, no nulos, valores aceptados) que se ejecutan en cada despliegue.
  • Documentación autogenerada: dbt genera un sitio web completo con la documentación de cada modelo, columna y el lineage graph de tu pipeline.
  • Materialización flexible: cada modelo puede ser una vista, una tabla, un modelo incremental o una snapshot, simplemente cambiando una configuración.

¿Por qué DuckDB?

DuckDB es una base de datos analítica embebida (in-process), similar a lo que SQLite es para OLTP pero optimizada para cargas analíticas OLAP. Sus ventajas para aprender dbt son evidentes:

  • Sin servidor: se ejecuta como un archivo local, sin instalar ni configurar ningún servicio.
  • Rápida: motor columnar optimizado para consultas analíticas.
  • Compatible: el adaptador dbt-duckdb soporta todas las funcionalidades de dbt-core.
  • Portable: tu base de datos es un solo archivo .duckdb que puedes mover o compartir.

Esta combinación convierte a dbt + DuckDB en el entorno ideal para prototipado rápido, aprendizaje y proyectos de analítica local donde no necesitas (ni quieres) levantar un Snowflake o BigQuery.

🚀 ¿Quieres dominar SQL antes de entrar en dbt?

dbt se basa enteramente en SQL. Si necesitas reforzar tus habilidades, el curso Introduction to SQL de DataCamp te dará las bases que necesitas: SELECT, JOIN, agregaciones y subconsultas. Una base sólida en SQL es el requisito fundamental para sacar el máximo partido a dbt.

2. Arquitectura del proyecto: staging, intermediate y marts

Arquitectura de modelos dbt en 3 capas: Seeds, Staging (views), Intermediate (views) y Marts (tables)

Un proyecto dbt bien diseñado organiza sus modelos en capas que reflejan el nivel de transformación de los datos. Esta arquitectura, recomendada por dbt Labs, separa las responsabilidades y facilita el mantenimiento:

┌─────────────────────────────────────────────────────────────────────────┐
  SEEDS (CSV)                                                          
  raw_customers.csv  ·  raw_products.csv  ·  raw_orders.csv            
└─────────────────────────────────┬───────────────────────────────────────┘
                                 
                                 
┌─────────────────────────────────────────────────────────────────────────┐
  STAGING (Views) — Limpieza y normalización                         
  stg_customers  ·  stg_products  ·  stg_orders                        
└─────────────────────────────────┬───────────────────────────────────────┘
                                 
                                 
┌─────────────────────────────────────────────────────────────────────────┐
  INTERMEDIATE (Views) — Lógica de negocio y enriquecimiento          
  int_orders_enriched (joins + cálculos financieros)                   
└─────────────────────────────────┬───────────────────────────────────────┘
                                 
                                 
┌─────────────────────────────────────────────────────────────────────────┐
  MARTS (Tables) — Modelos finales para consumo                     
  fct_orders (hechos)  ·  dim_customers (dimensión)                    
└─────────────────────────────────────────────────────────────────────────┘
Capa Materialización Propósito Convención de nombre
Staging view Limpieza, renombrado, casting de tipos. Relación 1:1 con cada fuente. stg_<fuente>
Intermediate view JOINs entre fuentes, cálculos de negocio, enriquecimiento. int_<descripción>
Marts table Modelos finales listos para dashboards y reportes. Hechos y dimensiones. fct_ / dim_

Esta separación no es capricho. Cada capa tiene un contrato claro: staging no contiene lógica de negocio, intermediate no se expone directamente a herramientas BI, y marts son la única interfaz para los consumidores finales. Si un analista necesita modificar un cálculo de revenue, sabe exactamente dónde buscar.

3. Instalación y configuración del entorno

Requisitos previos

  • Python (3.9 a 3.11) instalado en tu sistema
  • pip (gestor de paquetes de Python)
  • Git (opcional, pero recomendado para versionado)
  • Docker (opcional, para el despliegue containerizado)

Opción A: Instalación local (recomendada para aprender)

# Crear un entorno virtual (buena práctica)
python -m venv dbt-env
source dbt-env/bin/activate    # Linux/Mac
# dbt-env\Scripts\activate     # Windows

# Instalar dbt con el adaptador de DuckDB
pip install dbt-core==1.8.0 dbt-duckdb==1.8.0

# Verificar la instalación
dbt --version

La salida debería mostrar algo como:

Core:
  - installed: 1.8.0
  - latest:    1.8.x - Up to date!

Plugins:
  - duckdb: 1.8.0 - Up to date!

Opción B: Con Docker (un solo comando)

Si prefieres no instalar nada en tu máquina, el proyecto incluye un Dockerfile que configura todo automáticamente. Lo veremos en la sección de Docker.

4. Tu primer proyecto dbt desde cero

Flujo de ejecución dbt: deps, seed, run, test y docs generate con resultados del pipeline

4.1 Estructura del proyecto

Puedes obtener el proyecto de nuestro repositorio público de github: GitHub - dataprix/dbt-demo · GitHub

Para clonarlo e instalar todo lo necesario antes de ejecutar dbt:

git clone https://github.com/dataprix/dbt-demo.git
cd dbt-demo
pip install -r requirements.txt
dbt deps
dbt seed

Nuestro proyecto de demostración simula una empresa de venta de equipamiento tecnológico y mobiliario de oficina con datos de clientes, productos y pedidos. Veamos la estructura completa:

dbt-demo/
├── dbt_project.yml          # Configuración principal del proyecto
├── profiles.yml             # Conexión a la base de datos
├── packages.yml             # Dependencias externas (dbt_utils)
├── seeds/                   # Datos de ejemplo (CSV)
│   ├── raw_customers.csv    # 10 clientes de LATAM y España
│   ├── raw_products.csv     # 12 productos tech y mobiliario
│   └── raw_orders.csv       # 50 pedidos del año 2023
├── models/
│   ├── staging/             # Capa de limpieza
│   ├── intermediate/        # Capa de lógica de negocio
│   └── marts/               # Capa de consumo final
├── macros/                  # Funciones SQL reutilizables
└── analyses/                # Consultas analíticas ad-hoc

4.2 Configuración principal: dbt_project.yml

Este archivo es el corazón de tu proyecto dbt. Define el nombre, las rutas y la materialización por defecto de cada capa:

name: 'dataprix_demo'
version: '1.0.0'
config-version: 2

profile: 'dataprix_demo'

model-paths: ["models"]
analysis-paths: ["analyses"]
test-paths: ["tests"]
seed-paths: ["seeds"]
macro-paths: ["macros"]

models:
  dataprix_demo:
    staging:
      +materialized: view       # Las vistas no ocupan espacio
      +schema: staging
    intermediate:
      +materialized: view
      +schema: intermediate
    marts:
      +materialized: table      # Las tablas persisten los resultados
      +schema: marts

Observa cómo staging e intermediate se materializan como views (ligeras, se recalculan al consultarlas), mientras que marts se materializa como tables (datos persistidos, consultas rápidas para BI).

4.3 Perfil de conexión: profiles.yml

dbt necesita saber cómo conectarse a tu base de datos. Con DuckDB es tan simple como indicar la ruta de un archivo:

dataprix_demo:
  target: dev
  outputs:
    dev:
      type: duckdb
      path: dataprix_demo.duckdb    # Un solo archivo, sin servidor
      threads: 4                     # Paralelismo de ejecución

Compara esto con un perfil de Snowflake o BigQuery que requiere credenciales, proyecto, región, cuenta... Con DuckDB, un archivo es toda tu base de datos.

4.4 Datos de ejemplo: seeds

Los seeds en dbt son archivos CSV que se cargan directamente en la base de datos con dbt seed. Son ideales para datos de referencia, catálogos o datos de prueba. Nuestro dataset incluye:

Archivo Registros Descripción
raw_customers.csv 10 Clientes de España, México, Argentina, Colombia y Chile, con segmentos Premium, Standard y Gold.
raw_products.csv 12 Productos en categorías: Electrónica, Mobiliario, Iluminación y Accesorios, con precios y costes.
raw_orders.csv 50 Pedidos de enero a octubre de 2023, con descuentos, canales (online/store) y estados variados.
# Cargar los CSV en DuckDB
dbt seed

# Resultado:
# 04:12:15  Running with dbt=1.8.0
# 04:12:15  Found 6 models, 3 seeds, 21 tests
# 04:12:15  1 of 3 START seed file staging.raw_customers ........... [RUN]
# 04:12:15  1 of 3 OK loaded seed file staging.raw_customers ....... [INSERT 10]
# 04:12:15  2 of 3 OK loaded seed file staging.raw_orders .......... [INSERT 50]
# 04:12:15  3 of 3 OK loaded seed file staging.raw_products ........ [INSERT 12]
# 04:12:15  Finished running 3 seeds in 0.52s
# 04:12:15  Completed successfully
#
# Done. PASS=3 WARN=0 ERROR=0 SKIP=0 TOTAL=3

5. Capa 1: Modelos de staging

La capa de staging es el primer punto de contacto con los datos crudos. Su objetivo es simple y claro: limpiar, renombrar y normalizar sin añadir lógica de negocio. Cada modelo de staging tiene una relación 1:1 con una fuente de datos.

stg_orders.sql: Normalización de pedidos

with source as (
    select * from {{ ref('raw_orders') }}
),

renamed as (
    select
        order_id,
        customer_id,
        product_id,
        cast(order_date as date) as order_date,
        quantity,
        discount_pct / 100.0 as discount_rate,   -- Convertir % a decimal
        lower(status) as status,                   -- Normalizar a minúsculas
        lower(channel) as channel,
        current_timestamp as _loaded_at
    from source
)

select * from renamed

Observa varios patrones importantes:

  • {{ ref('raw_orders') }}: la función ref() es el corazón de dbt. En lugar de escribir nombres de tabla directamente, usas referencias que dbt resuelve automáticamente, generando el grafo de dependencias.
  • CTE pattern: separar source y renamed en CTEs (Common Table Expressions) mejora la legibilidad.
  • Transformaciones mínimas: solo casting de tipos, normalización de strings y conversiones de unidades. Sin JOINs, sin cálculos de negocio.
  • Campo _loaded_at: metadato de auditoría que indica cuándo se procesó el registro.

stg_customers.sql: Limpieza de clientes

with source as (
    select * from {{ ref('raw_customers') }}
),

renamed as (
    select
        customer_id,
        first_name,
        last_name,
        first_name || ' ' || last_name as full_name,  -- Campo derivado útil
        lower(email) as email,                          -- Emails siempre en minúsculas
        country,
        city,
        cast(signup_date as date) as signup_date,
        upper(segment) as segment,                      -- Segmentos en mayúsculas
        current_timestamp as _loaded_at
    from source
)

select * from renamed

stg_products.sql: Productos con métricas base

with source as (
    select * from {{ ref('raw_products') }}
),

renamed as (
    select
        product_id,
        product_name,
        category,
        subcategory,
        price,
        cost,
        price - cost as gross_margin,                         -- Margen absoluto
        round((price - cost) / price * 100, 2) as margin_pct, -- Margen porcentual
        supplier,
        current_timestamp as _loaded_at
    from source
)

select * from renamed

En este modelo hacemos una excepción calculando gross_margin y margin_pct en staging. Esto es aceptable porque son atributos intrínsecos del producto, no lógica de negocio que dependa de otros modelos.

📚 Domina los CTEs y JOINs que usa dbt

Los modelos dbt utilizan extensivamente CTEs (WITH ... AS), JOINs y funciones de ventana. El curso Intermediate SQL de DataCamp profundiza exactamente en estos temas, llevándote del SELECT básico a consultas profesionales como las que ves en este tutorial.

6. Capa 2: Modelos intermediate

La capa intermediate es donde ocurre la magia del negocio. Aquí combinamos las fuentes ya limpias, hacemos JOINs y calculamos métricas que reflejan la lógica real de la empresa. Estos modelos nunca se exponen directamente a herramientas BI: son piezas internas del pipeline.

int_orders_enriched.sql: El modelo central del pipeline

with orders as (
    select * from {{ ref('stg_orders') }}
),

customers as (
    select * from {{ ref('stg_customers') }}
),

products as (
    select * from {{ ref('stg_products') }}
),

enriched as (
    select
        o.order_id,
        o.order_date,
        date_trunc('month', o.order_date) as order_month,
        date_trunc('quarter', o.order_date) as order_quarter,
        extract(year from o.order_date) as order_year,

        -- Información del cliente
        o.customer_id,
        c.full_name as customer_name,
        c.segment as customer_segment,
        c.country as customer_country,
        c.city as customer_city,

        -- Información del producto
        o.product_id,
        p.product_name,
        p.category as product_category,
        p.subcategory as product_subcategory,

        -- Detalles del pedido
        o.quantity,
        o.discount_rate,
        o.status,
        o.channel,

        -- Cálculos financieros
        p.price as unit_price,
        p.cost as unit_cost,
        p.price * o.quantity as gross_revenue,
        p.price * o.quantity * (1 - o.discount_rate) as net_revenue,
        p.cost * o.quantity as total_cost,
        p.price * o.quantity * (1 - o.discount_rate)
            - p.cost * o.quantity as gross_profit,

        -- Margen sobre ingreso neto
        case
            when p.price * o.quantity * (1 - o.discount_rate) > 0
            then round(
                (p.price * o.quantity * (1 - o.discount_rate) - p.cost * o.quantity)
                / (p.price * o.quantity * (1 - o.discount_rate)) * 100, 2
            )
            else 0
        end as margin_pct

    from orders o
    left join customers c on o.customer_id = c.customer_id
    left join products p on o.product_id = p.product_id
)

select * from enriched

Este modelo es un excelente ejemplo de por qué la capa intermediate existe. Combina tres fuentes y calcula métricas que no pertenecen a ninguna fuente individual:

  • gross_revenue: precio × cantidad, antes de descuento.
  • net_revenue: lo que realmente factura la empresa, después de aplicar descuentos.
  • gross_profit: la diferencia entre lo facturado y el coste de los productos.
  • margin_pct: porcentaje de beneficio sobre el ingreso neto.
  • Granularidades temporales: mes, trimestre y año para facilitar agregaciones futuras.

7. Capa 3: Modelos marts (hechos y dimensiones)

Los marts son la interfaz pública de tu pipeline. Son los modelos que conectas a Metabase, Looker, Power BI o cualquier herramienta de visualización. Seguimos el patrón dimensional clásico con tablas de hechos (fct_) y dimensiones (dim_).

Modelo dimensional: tabla de hechos fct_orders y dimensión dim_customers con métricas de lifetime value

fct_orders.sql: Tabla de hechos de pedidos

with enriched_orders as (
    select * from {{ ref('int_orders_enriched') }}
),

final as (
    select
        order_id,
        order_date,
        order_month,
        order_quarter,
        order_year,

        customer_id,
        customer_name,
        customer_segment,
        customer_country,

        product_id,
        product_name,
        product_category,

        quantity,
        discount_rate,
        status,
        channel,

        unit_price,
        unit_cost,
        gross_revenue,
        net_revenue,
        total_cost,
        gross_profit,
        margin_pct,

        -- Flags booleanos para filtrado rápido en BI
        case when status = 'completed' then true else false end as is_completed,
        case when discount_rate > 0 then true else false end as has_discount,
        case when channel = 'online' then true else false end as is_online

    from enriched_orders
)

select * from final

Observa los flags booleanos al final: is_completed, has_discount, is_online. Estos campos facilitan enormemente la creación de filtros en herramientas BI sin que el analista tenga que recordar los valores exactos de cada campo.

dim_customers.sql: Dimensión de clientes con lifetime value

with customers as (
    select * from {{ ref('stg_customers') }}
),

orders as (
    select
        customer_id,
        count(*) as total_orders,
        count(case when status = 'completed' then 1 end) as completed_orders,
        sum(case when status = 'completed' then net_revenue else 0 end) as total_revenue,
        sum(case when status = 'completed' then gross_profit else 0 end) as total_profit,
        min(order_date) as first_order_date,
        max(order_date) as last_order_date,
        avg(case when status = 'completed' then net_revenue end) as avg_order_value
    from {{ ref('int_orders_enriched') }}
    group by 1
),

final as (
    select
        c.customer_id,
        c.full_name,
        c.email,
        c.country,
        c.city,
        c.segment,
        c.signup_date,

        -- Métricas de pedidos
        coalesce(o.total_orders, 0) as total_orders,
        coalesce(o.completed_orders, 0) as completed_orders,
        coalesce(o.total_revenue, 0) as lifetime_revenue,
        coalesce(o.total_profit, 0) as lifetime_profit,
        o.first_order_date,
        o.last_order_date,
        coalesce(o.avg_order_value, 0) as avg_order_value,

        -- Clasificación por valor
        case
            when coalesce(o.total_revenue, 0) >= 2000 then 'VIP'
            when coalesce(o.total_revenue, 0) >= 1000 then 'High Value'
            when coalesce(o.total_revenue, 0) >= 500 then 'Medium Value'
            else 'Low Value'
        end as value_tier,

        -- Recencia
        case
            when o.last_order_date is not null
            then cast(current_date - o.last_order_date as integer)
            else null
        end as days_since_last_order

    from customers c
    left join orders o on c.customer_id = o.customer_id
)

select * from final

Este modelo demuestra un patrón potente: enriquecer una dimensión con métricas agregadas. El resultado es una tabla donde cada fila es un cliente con todo su contexto (datos maestros + comportamiento de compra + clasificación). Esto permite a los analistas segmentar clientes en un dashboard con un simple filtro sobre value_tier.

Ejecutar el pipeline completo

# Ejecutar todos los modelos
dbt run

# Resultado:
# 04:12:20  Running with dbt=1.8.0
# 04:12:20  Found 6 models, 3 seeds, 21 tests
#
# 04:12:20  1 of 6 START sql view model staging.stg_customers ........ [RUN]
# 04:12:20  1 of 6 OK created sql view model staging.stg_customers ... [OK]
# 04:12:20  2 of 6 START sql view model staging.stg_orders ........... [RUN]
# 04:12:20  2 of 6 OK created sql view model staging.stg_orders ...... [OK]
# 04:12:20  3 of 6 START sql view model staging.stg_products ......... [RUN]
# 04:12:20  3 of 6 OK created sql view model staging.stg_products .... [OK]
# 04:12:20  4 of 6 START sql view model intermediate.int_orders_enriched [RUN]
# 04:12:20  4 of 6 OK created sql view model intermediate.int_orders_enriched [OK]
# 04:12:20  5 of 6 START sql table model marts.fct_orders ............ [RUN]
# 04:12:20  5 of 6 OK created sql table model marts.fct_orders ....... [OK]
# 04:12:20  6 of 6 START sql table model marts.dim_customers ......... [RUN]
# 04:12:20  6 of 6 OK created sql table model marts.dim_customers .... [OK]
#
# 04:12:21  Finished running 4 view models, 2 table models in 1.02s
# 04:12:21  Completed successfully
#
# Done. PASS=6 WARN=0 ERROR=0 SKIP=0 TOTAL=6

6 modelos ejecutados, 0 errores. Observa cómo dbt respeta automáticamente el orden de dependencias: primero staging, después intermediate, finalmente marts. No necesitas gestionar el orden manualmente.

8. Tests de calidad de datos

Resultados de tests de calidad de datos: 21 validaciones automáticas pasadas en 6 modelos dbt

Una de las mayores fortalezas de dbt es su sistema de testing integrado. Los tests se definen en archivos YAML junto a la documentación de cada modelo, siguiendo una filosofía de "documentación como código".

Tipos de tests disponibles

Test Qué valida Ejemplo
unique No hay valores duplicados Claves primarias como order_id
not_null No hay valores nulos Campos obligatorios como customer_id
accepted_values Solo valores permitidos Status: completed, cancelled, refunded
relationships Integridad referencial Cada customer_id en pedidos existe en clientes

Ejemplo real: schema de staging

version: 2

models:
  - name: stg_orders
    description: "Pedidos limpios y normalizados desde la fuente raw"
    columns:
      - name: order_id
        description: "Identificador único del pedido"
        tests:
          - unique
          - not_null
      - name: customer_id
        description: "Referencia al cliente"
        tests:
          - not_null
      - name: status
        description: "Estado del pedido"
        tests:
          - accepted_values:
              values: ['completed', 'cancelled', 'refunded', 'pending']

  - name: stg_customers
    description: "Clientes limpios y normalizados"
    columns:
      - name: customer_id
        tests:
          - unique
          - not_null
      - name: email
        description: "Email del cliente (normalizado a minúsculas)"
        tests:
          - unique
          - not_null
      - name: segment
        tests:
          - accepted_values:
              values: ['PREMIUM', 'STANDARD', 'GOLD']

Ejecutar los tests

# Ejecutar todos los tests
dbt test

# Resultado:
# 04:12:25  Running with dbt=1.8.0
# 04:12:25  Found 6 models, 3 seeds, 21 tests
#
# 04:12:26   1 of 21 PASS unique_stg_orders_order_id ................ [PASS]
# 04:12:26   2 of 21 PASS not_null_stg_orders_order_id .............. [PASS]
# 04:12:26   3 of 21 PASS not_null_stg_orders_customer_id ........... [PASS]
# 04:12:26   4 of 21 PASS not_null_stg_orders_product_id ............ [PASS]
# 04:12:26   5 of 21 PASS accepted_values_stg_orders_status ......... [PASS]
#   ...
# 04:12:26  21 of 21 PASS accepted_values_dim_customers_value_tier .. [PASS]
#
# 04:12:26  Finished running 21 tests in 0.85s
# 04:12:26  Completed successfully
#
# Done. PASS=21 WARN=0 ERROR=0 SKIP=0 TOTAL=21

21 tests ejecutados, todos pasados. Estos tests se ejecutan automáticamente en cada dbt build o se integran en tu pipeline de CI/CD, garantizando que ningún cambio rompa la integridad de los datos.

9. Documentación y lineage graph

Lineage graph de dbt Docs mostrando el flujo de datos desde seeds hasta marts con dependencias entre modelos

dbt genera automáticamente un sitio web de documentación que incluye:

  • Descripción de cada modelo y columna (desde los archivos YAML).
  • El SQL compilado de cada modelo.
  • Los tests asociados.
  • Un lineage graph interactivo que muestra cómo fluyen los datos desde las fuentes hasta los marts.
# Generar la documentación
dbt docs generate

# Resultado:
# 04:12:30  Running with dbt=1.8.0
# 04:12:30  Building catalog
# 04:12:30  Catalog written to /usr/app/dbt/target/catalog.json

# Servir la documentación en un navegador
dbt docs serve --port 8080

# Abre http://localhost:8080 en tu navegador

El lineage graph

El lineage graph (grafo de linaje) es una de las características más valoradas de dbt. Muestra visualmente el flujo de datos desde las fuentes crudas hasta los modelos finales. En nuestro proyecto, el grafo tiene esta forma:

raw_customers ──> stg_customers ──┬──> int_orders_enriched ──┬──> fct_orders
                                 │                        │
raw_products  ──> stg_products  ──┘                        └──> dim_customersraw_orders    ──> stg_orders    ──────────────────────────────────────┘
                                                           (también usa stg_customers)

Este grafo te permite responder preguntas críticas de forma inmediata:

  • «Si cambio el campo discount_pct en raw_orders, ¿qué modelos se ven afectados?» — Todos los downstream: stg_ordersint_orders_enrichedfct_orders + dim_customers.
  • «¿De dónde viene el campo lifetime_revenue en dim_customers?» — Trazas hacia arriba: int_orders_enrichedstg_orders + stg_products → seeds originales.

En la interfaz web de dbt docs puedes hacer clic en cada nodo para ver su SQL, documentación y tests. El botón «Lineage Graph» en la esquina inferior derecha te muestra el grafo completo o filtrado por el modelo que estés consultando.

🐍 ¿Quieres llevar tu pipeline más allá con Python?

dbt se integra con Python para modelos avanzados de ML y análisis estadístico. El curso Introduction to Python de DataCamp es el punto de partida perfecto para combinar la potencia de SQL en dbt con scripts de Python para análisis exploratorio, visualización con pandas y modelos predictivos sobre tus datos transformados.

10. Despliegue con Docker

Diagrama de despliegue Docker con contenedor dbt, pipeline secuencial y documentación servida en puerto 8080

Docker nos permite empaquetar todo el entorno dbt en un contenedor reproducible. Esto significa que cualquier persona del equipo puede ejecutar el pipeline exactamente igual, sin preocuparse por versiones de Python, dependencias o configuración del sistema.

Dockerfile

FROM python:3.11-slim

WORKDIR /usr/app/dbt

# Instalar dependencias del sistema
RUN apt-get update && apt-get install -y git \
    && rm -rf /var/lib/apt/lists/*

# Instalar dbt
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copiar el proyecto
COPY . .

# Configurar el perfil de dbt
RUN mkdir -p /root/.dbt && cp profiles.yml /root/.dbt/profiles.yml

CMD ["dbt", "run"]

docker-compose.yml

version: '3.8'

services:
  dbt:
    build: .
    container_name: dbt-demo
    volumes:
      - .:/usr/app/dbt
      - ./target:/usr/app/dbt/target
      - ./logs:/usr/app/dbt/logs
    environment:
      - DBT_PROFILES_DIR=/root/.dbt
    command: >
      sh -c "
        dbt deps &&
        dbt seed &&
        dbt run &&
        dbt test &&
        dbt docs generate &&
        echo 'SUCCESS: dbt pipeline completed!' &&
        dbt docs serve --host 0.0.0.0 --port 8080
      "
    ports:
      - "8080:8080"

Ejecutar con un solo comando

# Construir y ejecutar el pipeline completo
docker-compose up --build

# El comando ejecuta secuencialmente:
# 1. dbt deps      → Instala paquetes (dbt_utils)
# 2. dbt seed      → Carga los CSV en DuckDB
# 3. dbt run       → Ejecuta los 6 modelos
# 4. dbt test      → Valida los 21 tests
# 5. dbt docs generate → Genera documentación
# 6. dbt docs serve    → Sirve la docs en http://localhost:8080

Tras ejecutar el comando, abre http://localhost:8080 en tu navegador para explorar la documentación interactiva y el lineage graph de tu proyecto.

11. Conclusión y próximos pasos

En este tutorial hemos construido un proyecto dbt completo que incluye:

  • 3 seeds con datos realistas de una empresa ficticia de equipamiento de oficina.
  • 6 modelos SQL organizados en tres capas (staging → intermediate → marts) siguiendo las mejores prácticas de dbt Labs.
  • 21 tests de calidad que validan unicidad, no nulos y valores aceptados en cada modelo.
  • Documentación autogenerada con lineage graph interactivo.
  • Despliegue Docker reproducible con un solo comando.

¿Qué puedes hacer a continuación?

Siguiente paso Descripción
Modelos incrementales Usa +materialized: incremental para procesar solo datos nuevos en lugar de reconstruir tablas completas.
Snapshots Captura cambios históricos (SCD Type 2) en dimensiones como clientes o productos.
Macros personalizados Crea funciones Jinja reutilizables para patrones SQL que se repiten en tu proyecto.
dbt Cloud o CI/CD Automatiza la ejecución con dbt Cloud, GitHub Actions o Airflow para pipelines en producción.
Conectar a un warehouse real Migra este proyecto a BigQuery, Snowflake o Redshift cambiando únicamente el profiles.yml.

El código completo de este proyecto está disponible para descargar y ejecutar. La combinación de dbt + DuckDB + Docker ofrece un entorno de aprendizaje y prototipado que puedes tener funcionando en minutos, sin depender de servicios cloud ni configuraciones complicadas.

🎓 Forma tu equipo en datos con DataCamp

Si este tutorial te ha resultado útil, estos cursos de DataCamp te ayudarán a profundizar en las habilidades que necesitas para trabajar profesionalmente con pipelines de datos:


Este artículo ha sido elaborado por el equipo de Dataprix. Todos los ejemplos de código están probados y disponibles para su descarga.