Migrating Northwind's legacy MySQL reporting to a modern analytical warehouse on Snowflake, using dlt, dbt, and Airflow.
MySQL (OLTP) → dlt → S3 (raw Parquet) → Snowflake RAW
→ dbt (staging → dims/facts → marts) → BI Dashboard
Airflow orchestrates. GitHub Actions runs CI.
- Source: Northwind MySQL database (Docker)
- Extraction: dlt pipelines with incremental loading on
modified_at - Landing: Amazon S3 as partitioned Parquet files
- Warehouse: Snowflake (RAW_DB, ANALYTICS_DB, DEV_DB)
- Transformation: dbt Core (staging → dimensions/facts → marts/OBT)
- Orchestration: Airflow 3 + Cosmos for dbt integration
- BI: Streamlit dashboard with RBAC (REPORTER role)
| Layer | Tool |
|---|---|
| Source | MySQL 8 (Docker) |
| Extraction | dlt |
| Landing | Amazon S3 |
| Warehouse | Snowflake |
| Transformation | dbt Core |
| Orchestration | Airflow 3 + Cosmos |
| CI/CD | GitHub Actions |
| BI Dashboard | Streamlit |
- Docker & Docker Compose
- Python 3.10+
- Snowflake account
- AWS account (S3)
- Airflow (via Astronomer CLI recommended)
docker compose up -d
# Access Adminer at http://localhost:8081
# Server: mysql, User: northwind, Password: northwind, DB: northwindcp .env.example .env
# Edit .env with your Snowflake, AWS, and MySQL credentials# Run SQL scripts in order (or use Terraform)
cd infra/snowflake
# Execute 01_databases.sql through 07_snowpipe.sqlpip install -r requirements.txt
pip install -r airflow/requirements.txt
pip install -r dashboard/requirements.txtcd ingestion
python pipeline.pycd dbt_modeling
dbt debug
dbt run
dbt testcd airflow
astro dev start
# Access at http://localhost:8080cd dashboard
streamlit run app.pynorthwind project/
├── ingestion/ # dlt pipeline (MySQL → S3)
├── dbt_modeling/ # dbt project (transformations)
├── airflow/ # Airflow DAGs + Cosmos config
├── dashboard/ # Streamlit BI dashboard
├── infra/
│ ├── snowflake/ # Manual DDL scripts
│ └── terraform/ # Optional IaC
├── mysql/ # MySQL init scripts + data generator
├── datasets/ # CSV source data
├── docs/ # Architecture, data dictionary, cost analysis
├── scripts/ # Utility scripts
└── snowflake_keys/ # RSA keys for key-pair auth
The marts/OBT models answer:
| Business Process | Key Questions |
|---|---|
| Sales Overview | Revenue by period, top products/customers, sales rep performance |
| Product Inventory | Stock levels, turnover, reorder points, supplier lead times |
| Customer Reporting | Customer lifetime value, order frequency, geographic distribution |
- Incremental Loading: dlt uses
modified_atfor CDC; small dimension tables full-refresh - RBAC: Snowflake roles (TRANSFORMER, LOADER, REPORTER) with key-pair authentication
- Data Quality: dbt tests (unique, not_null, referential integrity, custom)
- CI/CD: GitHub Actions for linting, testing, and deployment
- Cost Monitoring: Query history analysis per dbt model (see
docs/cost_analysis.md)
- Architecture - Detailed diagram and flow
- Data Dictionary - Generated from dbt docs
- Cost Analysis - Snowflake credit usage by model
See the Notion build tracker. Minimum viable slice = tasks 1-12, 13-17, 21-30, 35-45, 59-60.
- MySQL source with seed data (Docker)
- dlt ingestion pipeline (MySQL → S3)
- Snowflake infrastructure (databases, warehouses, roles, storage integration)
- dbt models (staging → dims/facts → marts)
- Airflow DAGs (ingestion + transform, wired via Assets)
- Streamlit dashboard skeleton
- Documentation structure
- Dashboard charts and queries
- Full test coverage
- GitHub Actions CI/CD
- Terraform for Snowflake
MIT


.gif)