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
- Qué es dbt y por qué debería importarte
- Arquitectura del proyecto: staging, intermediate y marts
- Instalación y configuración del entorno
- Tu primer proyecto dbt desde cero
- Capa 1: Modelos de staging
- Capa 2: Modelos intermediate
- Capa 3: Modelos marts (hechos y dimensiones)
- Tests de calidad de datos
- Documentación y lineage graph
- Despliegue con Docker
- 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-duckdbsoporta todas las funcionalidades de dbt-core. - Portable: tu base de datos es un solo archivo
.duckdbque 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
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 --versionLa 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
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-hoc4.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: martsObserva 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ónCompara 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=35. 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 renamedObserva varios patrones importantes:
{{ ref('raw_orders') }}: la funciónref()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
sourceyrenameden 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 renamedstg_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 renamedEn 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 enrichedEste 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_).
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 finalObserva 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 finalEste 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=66 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
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=2121 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
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 navegadorEl 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_customers ↑ raw_orders ──> stg_orders ──────────────────────────────────────┘ (también usa stg_customers)
Este grafo te permite responder preguntas críticas de forma inmediata:
- «Si cambio el campo
discount_pctenraw_orders, ¿qué modelos se ven afectados?» — Todos los downstream:stg_orders→int_orders_enriched→fct_orders+dim_customers. - «¿De dónde viene el campo
lifetime_revenueendim_customers?» — Trazas hacia arriba:int_orders_enriched→stg_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
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:8080Tras 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:
- Introduction to SQL — Las bases de SELECT, WHERE, JOIN y GROUP BY.
- Intermediate SQL — CTEs, subqueries, funciones de ventana y CASE.
- Introduction to Python — Python para análisis de datos, pandas y visualización.
- Database Design — Modelado dimensional, normalización y esquemas estrella.
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.
