
Ecommerce Data Warehouse
- 66 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Build a commerce data warehouse with star-schema tables, ETL pipelines, and dbt models on BigQuery, Snowflake, or Redshift.
About
Guides extracting store, ad, and shipping data into a warehouse (BigQuery recommended) and transforming it with dbt for BI dashboards. A developer uses it when platform analytics can't answer questions or data must combine across channels.
- Warehouse selection guidance with BigQuery free-tier rationale
- Star-schema plus dbt transforms feeding Looker Studio, Metabase, or Tableau
Ecommerce Data Warehouse by the numbers
- 66 all-time installs (skills.sh)
- Ranked #370 of 911 Databases skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill ecommerce-data-warehouseAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 66 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Build a commerce data warehouse with star-schema tables, ETL pipelines, and dbt models on BigQuery, Snowflake, or Redshift.
Files
E-commerce Data Warehouse
Overview
An ecommerce data warehouse centralizes data from your store, ad platforms, shipping carriers, and accounting system into a single analytics layer — enabling reports that no single platform can produce on its own. Most merchants start needing a warehouse when they outgrow their platform's built-in analytics (typically around $1M+ ARR or when managing multiple channels), want to combine ad spend with order data for true ROAS, or need SQL-level access to run custom cohort and attribution analyses.
This skill guides you through extracting data from your platform, loading it into BigQuery (recommended for cost), and transforming it with dbt so your team can build dashboards in Looker Studio, Metabase, or Tableau.
When to Use This Skill
- When your platform's built-in analytics cannot answer important business questions
- When you need to combine data from multiple sources (Shopify + Meta Ads + Google Ads + shipping)
- When you want to build custom cohort retention, LTV, and attribution models
- When analysts or data scientists need SQL access to raw order data
- When you need a single source of truth across multiple sales channels
Core Instructions
Step 1: Choose your warehouse and set up the infrastructure
For most ecommerce merchants, BigQuery is the best starting point:
- Free tier: first 10 GB storage + 1 TB queries per month free
- No infrastructure to manage — serverless
- Excellent integrations with Looker Studio (free BI tool), dbt, and every major data integration tool
Alternatives:
- Snowflake: Better for teams that need time-travel, data sharing, or advanced concurrency; more expensive at small scale
- Redshift: Good if you are already in AWS; more operational overhead than BigQuery or Snowflake
Set up BigQuery: 1. Go to console.cloud.google.com and create a new Google Cloud project 2. Enable the BigQuery API 3. Create a dataset (e.g., ecommerce_raw) for raw ingested data and a second dataset (ecommerce_analytics) for transformed tables
Step 2: Extract data from your ecommerce platform
The easiest way to get platform data into BigQuery is a managed connector. Avoid building custom extractors unless you have a specific requirement.
---
Shopify
Option A: Fivetran (recommended, $$$) 1. Sign up at fivetran.com 2. Add the Shopify connector — enter your shop URL and grant API access 3. Fivetran syncs orders, customers, products, inventory, and financial data incrementally to BigQuery 4. Pre-built dbt package: dbt-fivetran/shopify — provides staging and mart models out of the box
Option B: Stitch (mid-price) 1. Sign up at stitchdata.com 2. Add the Shopify integration — select tables to sync (orders, order_line_items, customers, products) 3. Stitch loads raw data to BigQuery; you transform with dbt
Option C: Shopify's native data export (free, manual) 1. Go to Analytics → Reports → [any report] and click Export — available for orders, customers, products 2. For bulk data: Go to Settings → Bulk operations or use the Shopify Admin API bulk query to export all data as JSONL 3. Load JSONL files to BigQuery using bq load CLI command or Google Cloud Storage
Option D: Polar Analytics (simplest, all-in-one)
- Install Polar Analytics from the Shopify App Store — it connects Shopify + all ad platforms + shipping and provides a pre-built BigQuery export with a standard schema; skip building the pipeline yourself
---
WooCommerce
Option A: Stitch or Airbyte 1. Stitch: Does not have a native WooCommerce connector; use their MySQL connector to connect directly to your WordPress database 2. Airbyte (open source, self-hosted): Has a WooCommerce connector; run on a $20/month VM or use Airbyte Cloud
Option B: Direct MySQL replication WooCommerce stores all data in MySQL. For a technical team: 1. Enable binary log replication on your MySQL instance 2. Use Debezium or Airbyte's MySQL CDC connector to stream changes to BigQuery 3. This requires DevOps expertise but provides the most real-time data
Option C: Metorik export 1. Use Metorik to pull and transform WooCommerce data into clean CSVs 2. Schedule CSV exports and load to BigQuery via Cloud Functions or a simple Google Apps Script
---
BigCommerce
Option A: Fivetran BigCommerce connector
- BigCommerce is available as a Fivetran source; set up the same way as Shopify
Option B: BigCommerce Data Solutions
- BigCommerce offers a native Insights product (available on higher-tier plans) that provides pre-built analytics; check if this meets your needs before building a warehouse
Option C: Stitch BigCommerce connector
- Available in Stitch; syncs orders, customers, and products to BigQuery
---
Step 3: Install and configure dbt
dbt (data build tool) transforms raw ingested data into clean analytics-ready tables using SQL. It handles dependency management, testing, and documentation.
Install dbt:
pip install dbt-bigquery # for BigQuery
# or: pip install dbt-snowflake / dbt-redshiftInitialize a project:
dbt init ecommerce_analytics
cd ecommerce_analyticsConfigure your profile in `~/.dbt/profiles.yml` (points to your BigQuery project).
Step 4: Build dbt models for your ecommerce data
If using Fivetran + Shopify, install the pre-built Shopify dbt package:
# packages.yml
packages:
- package: fivetran/shopify
version: [">=0.10.0", "<0.11.0"]Run dbt deps to install, then dbt run to build all models.
For other platforms or custom schemas, build models in this three-layer pattern:
Staging layer — clean raw data, no business logic:
-- models/staging/stg_orders.sql
with source as (select * from {{ source('shopify_raw', 'order') }}),
renamed as (
select
id::varchar as order_id,
name as order_number,
customer_id::varchar as customer_id,
email,
financial_status,
fulfillment_status,
total_price::numeric(12,2) as total_price,
subtotal_price::numeric(12,2) as subtotal_price,
total_discounts::numeric(10,2) as total_discounts,
currency,
source_name as channel,
created_at as ordered_at
from source
where _fivetran_deleted = false
)
select * from renamedMart layer — business-ready tables for dashboards:
-- models/marts/fct_orders.sql
with orders as (select * from {{ ref('stg_orders') }}),
customers as (select * from {{ ref('stg_customers') }}),
first_orders as (
select customer_id, min(ordered_at) as first_order_at
from orders group by 1
)
select
o.order_id,
o.order_number,
o.customer_id,
o.ordered_at,
o.total_price,
o.subtotal_price,
o.total_discounts,
o.channel,
o.ordered_at = fo.first_order_at as is_first_order,
date_trunc(o.ordered_at, month) as order_month
from orders o
left join first_orders fo on o.customer_id = fo.customer_id
where o.financial_status not in ('voided', 'pending')Step 5: Add data quality tests
Add tests in your schema YAML files so broken pipelines are caught before they reach dashboards:
# models/marts/schema.yml
models:
- name: fct_orders
columns:
- name: order_id
tests: [unique, not_null]
- name: total_price
tests:
- not_null
- dbt_utils.accepted_range:
min_value: 0Run tests: dbt test
Step 6: Connect a BI tool to your warehouse
Once data is in BigQuery, connect a visualization layer:
- Google Looker Studio (free): Go to lookerstudio.google.com → Add data source → BigQuery → select your project and tables; build dashboards with drag-and-drop
- Metabase (open source, self-hosted): Install on a $10/month VM; connect to BigQuery; lets non-technical users browse data and build charts with a GUI
- Tableau, Looker, Power BI: Standard enterprise options; Looker is the most powerful for ecommerce analytics but has significant cost and setup overhead
Best Practices
- Start with managed connectors, not custom code — Fivetran or Stitch cost $200–$500/month but save 40+ engineering hours; the breakeven is fast unless you have a very low traffic store
- Use the dbt Shopify package if you are on Shopify + Fivetran — it provides production-quality models for 15+ commonly needed tables out of the box; no need to reinvent
- Implement Slowly Changing Dimensions (SCD Type 2) for products and customers — track historical changes so you can analyze orders with the attributes that were true at the time of purchase
- Build a date dimension table — pre-populate with a full calendar including fiscal periods, holidays, and week numbers; every fact table should join to it
- Add dbt tests before connecting dashboards — uniqueness, not-null, and referential integrity tests catch data pipeline failures before they produce wrong numbers in reports
- Separate staging, intermediate, and mart layers — staging cleans raw data, intermediate models join and enrich, marts are the final analytics-ready tables your BI tool queries
Common Pitfalls
| Problem | Solution |
|---|---|
| Revenue in warehouse does not match platform analytics | Verify currency handling (cents vs. dollars), discount application order, and tax inclusion/exclusion; build a daily reconciliation check that compares warehouse totals to platform API totals |
| Historical product prices not captured | Implement SCD Type 2 on the product dimension and join fact tables to the product row that was current at the time of the order |
| ETL fails mid-run leaving partial data | Use dbt's on_schema_change: sync_all_columns and atomic table swaps; for incremental models, configure a unique_key to handle re-runs idempotently |
| Dashboard queries are too slow | Pre-aggregate in daily summary fact tables; partition large tables by date in BigQuery; avoid running heavy SQL on load |
| Customer identity not resolved across channels | Build a customer identity resolution table that maps email, phone, and platform-specific IDs to a single canonical customer key |
Related Skills
- @attribution-modeling
- @customer-analytics
- @sales-reporting-dashboard
- @product-analytics
{
"context": "Tests whether the agent implements customer LTV, segmentation tiers, activity status logic, and daily KPI calculations following the prescribed thresholds and formulas, plus a customer identity resolution table.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Customer tier: one_time",
"max_score": 7,
"description": "Customer tier classification assigns 'one_time' to customers with exactly 1 total order"
},
{
"name": "Customer tier: repeat thresholds",
"max_score": 8,
"description": "Customer tier assigns 'repeat' for 2-3 orders and 'loyal' for 4-10 orders (both thresholds must be correct)"
},
{
"name": "Customer tier: champion",
"max_score": 7,
"description": "Customer tier assigns 'champion' for customers with more than 10 orders"
},
{
"name": "Activity status: churned threshold",
"max_score": 8,
"description": "Activity status assigns 'churned' when days since last order exceeds 365"
},
{
"name": "Activity status: at_risk and cooling",
"max_score": 8,
"description": "Activity status assigns 'at_risk' for >180 days and 'cooling' for >90 days since last order (both thresholds and labels must match)"
},
{
"name": "LTV 24-month formula",
"max_score": 10,
"description": "Predicted LTV uses the formula: (total_revenue / (customer_lifespan_days / 30.0)) * 24, cast to numeric"
},
{
"name": "LTV fallback for new customers",
"max_score": 8,
"description": "The LTV calculation falls back to total_revenue for customers with lifespan of 30 days or fewer"
},
{
"name": "KPI: gross_margin_pct formula",
"max_score": 8,
"description": "kpi_daily_summary calculates gross_margin_pct as (gross_profit / net_revenue * 100), guarded against division by zero"
},
{
"name": "KPI: avg_order_value formula",
"max_score": 8,
"description": "kpi_daily_summary calculates avg_order_value as (net_revenue / total_orders), guarded against division by zero"
},
{
"name": "KPI: returning_customer_pct",
"max_score": 8,
"description": "kpi_daily_summary calculates a returning customer percentage metric (returning_customers / unique_customers * 100 or equivalent)"
},
{
"name": "Cohort month field",
"max_score": 7,
"description": "The LTV model includes a cohort_month field derived from the customer's first order date truncated to month"
},
{
"name": "Customer identity mapping table",
"max_score": 13,
"description": "dim_customer_identity.sql defines a table or model that maps multiple identifiers (at minimum: email and at least one platform-specific ID) to a single canonical customer key"
}
]
}
Customer Analytics Module for Meadow & Mill Subscription Box
Problem/Feature Description
Meadow & Mill is a subscription box e-commerce company that ships curated artisan goods monthly. Their marketing team is drowning in raw order data but lacking structured customer intelligence. They need two things urgently: a customer lifetime value model to identify their most valuable buyers, and a daily KPI summary their executives can review each morning.
The marketing team wants customers segmented by their purchase behavior so campaigns can be targeted appropriately. They also need customers flagged by how recently they've engaged, so the retention team can prioritize outreach. The predicted lifetime value metric is the centerpiece of their upcoming board presentation — the CFO wants a 24-month revenue forecast per customer based on observed purchase velocity.
The analytics team also needs a daily aggregate model that powers the executive dashboard — showing revenue, margin, and customer mix metrics for each day. The company sells internationally so the per-customer data may come from multiple platforms; the data team wants a mapping layer that ensures each customer is represented by a single canonical key regardless of which channel they came in through.
Output Specification
Produce the following SQL files (written as dbt-style CTEs or plain SQL — your choice):
models/marts/kpi_customer_ltv.sql— customer lifetime value model with segmentationmodels/marts/kpi_daily_summary.sql— daily KPI aggregate modelmodels/marts/dim_customer_identity.sql— customer identity resolution mapping tableanalysis-notes.md— brief documentation explaining the segmentation thresholds, activity status logic, and the LTV formula used
Assume the following base tables/views are available:
fct_order_items— one row per order line item with columns: customer_key, order_id, ordered_at, net_revenue, gross_profit, quantity, discount_amount, is_first_orderdim_customers— customer dimension with: customer_key, customer_id, email, first_name, last_name, country, acquisition_channel, is_currentdim_date— date dimension with: date_key, full_date, day_name, week_of_year, month_name, quarter, year
{
"context": "Tests whether the agent structures a dbt project with the prescribed layer materialization settings, filters, macros, incremental strategy, and documentation patterns for e-commerce Shopify data.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Staging materialized as view",
"max_score": 8,
"description": "dbt_project.yml configures the staging layer with +materialized: view"
},
{
"name": "Intermediate materialized as ephemeral",
"max_score": 8,
"description": "dbt_project.yml configures the intermediate layer with +materialized: ephemeral"
},
{
"name": "Marts materialized as table",
"max_score": 8,
"description": "dbt_project.yml configures the marts layer with +materialized: table"
},
{
"name": "Staging schema separation",
"max_score": 7,
"description": "dbt_project.yml sets +schema: staging for the staging layer and +schema: analytics (or similar) for the marts layer"
},
{
"name": "Fivetran deleted filter",
"max_score": 8,
"description": "The staging orders model filters out soft-deleted records using the _fivetran_deleted column (e.g. WHERE _fivetran_deleted = false)"
},
{
"name": "Voided/pending order filter",
"max_score": 8,
"description": "The final fact model (fct_order_items) excludes orders with financial_status in ('voided', 'pending')"
},
{
"name": "UTM parameter extraction",
"max_score": 9,
"description": "The staging orders model extracts utm_source, utm_medium, and utm_campaign from the landing_site column (via macro, regex, or parsing function)"
},
{
"name": "Incremental fact model",
"max_score": 9,
"description": "The fct_order_items model uses incremental materialization (materialized='incremental' or config block) with a unique_key specified"
},
{
"name": "is_current join pattern",
"max_score": 7,
"description": "The fact model joins to dimension tables filtering on is_current = true"
},
{
"name": "Not_null and unique tests",
"max_score": 8,
"description": "schema.yml includes both unique and not_null tests on the primary key column of fct_order_items"
},
{
"name": "Referential integrity test",
"max_score": 8,
"description": "schema.yml includes a relationships test on a foreign key column (e.g. customer_key referencing dim_customers)"
},
{
"name": "Accepted range test on revenue",
"max_score": 8,
"description": "schema.yml includes a dbt_utils.accepted_range or accepted_values test with min_value: 0 on a revenue column"
},
{
"name": "Model descriptions documented",
"max_score": 4,
"description": "schema.yml includes description fields for at least the fct_order_items model and two or more of its columns"
}
]
}
dbt Transformation Project for Bloom & Basket Shopify Store
Problem/Feature Description
Bloom & Basket is an online floral subscription service running on Shopify. Their data engineering team recently onboarded Fivetran to sync Shopify data into their cloud data warehouse (BigQuery). The raw tables are landing in a source schema, but the analysts can't use them directly — the data is messy, fields need renaming, and there's no clean separation between raw ingestion and analytics-ready output.
The team wants a properly structured dbt project that transforms the raw Fivetran-synced Shopify data into analytics-ready models. They need a clear layered architecture so that analysts can trust the marts layer, and so the intermediate logic isn't exposed or materialized unnecessarily. The staging models should handle cleanup and normalization, including extracting marketing attribution information from URL parameters. The final order fact model should only include financially settled transactions. Data quality checks need to be in place on the key models, and all models should be documented.
The team is particularly concerned about the fact table growing too large — they want the transformation strategy to handle that gracefully so daily runs don't reprocess the entire history.
Output Specification
Produce a complete (but minimal) dbt project structure with the following files:
dbt_project.yml— project configuration with model layer settingsmodels/staging/stg_orders.sql— staging model for Shopify ordersmodels/staging/stg_order_items.sql— staging model for order line itemsmodels/marts/fct_order_items.sql— final fact model joining all dimensionsmodels/marts/schema.yml— documentation and tests for the marts modelsdesign-notes.md— brief explanation of materialization choices and incremental strategy
Input Files
The following files describe the raw Fivetran source tables. Extract them before beginning.
=============== FILE: inputs/source_schema.md ===============
Raw Source Tables (Fivetran Shopify connector)
Schema: shopify_raw
orders
| column | type | notes |
|---|---|---|
| id | STRING | Shopify order ID |
| name | STRING | Human-readable order number (e.g. #1001) |
| STRING | Customer email | |
| customer_id | STRING | Shopify customer ID |
| financial_status | STRING | paid, pending, voided, refunded |
| fulfillment_status | STRING | fulfilled, partial, null |
| total_price | STRING | Total in store currency |
| subtotal_price | STRING | Pre-tax, pre-shipping subtotal |
| total_discounts | STRING | Total discounts applied |
| total_tax | STRING | Tax collected |
| total_shipping_price_set_shop_money_amount | STRING | Shipping charged |
| currency | STRING | ISO currency code |
| source_name | STRING | Channel: web, pos, draft_orders |
| referring_site | STRING | Referring URL |
| landing_site | STRING | Landing page URL (may contain UTM params) |
| cancel_reason | STRING | Reason if cancelled |
| cancelled_at | TIMESTAMP | Cancellation time |
| created_at | TIMESTAMP | Order creation time |
| updated_at | TIMESTAMP | Last update time |
| _fivetran_deleted | BOOLEAN | Soft-delete flag set by Fivetran |
order_line_items
| column | type | notes |
|---|---|---|
| id | STRING | Line item ID |
| order_id | STRING | Parent order ID |
| product_id | STRING | Shopify product ID |
| variant_id | STRING | Product variant ID |
| sku | STRING | SKU code |
| title | STRING | Product title |
| variant_title | STRING | Variant description |
| quantity | INTEGER | Units ordered |
| price | STRING | Unit price as string |
| total_discount | STRING | Discount on this line item |
{
"context": "Tests whether the agent designs the e-commerce star schema following the prescribed dimensional modeling patterns, including correct SCD Type 2 implementation, fact table grain and measures, date key format, and monetary consistency documentation.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Date key as INTEGER",
"max_score": 8,
"description": "The date dimension's primary key is defined as INTEGER (not DATE or VARCHAR), reflecting the YYYYMMDD integer format"
},
{
"name": "SCD Type 2 on customer dim",
"max_score": 10,
"description": "The customer dimension includes all three SCD Type 2 fields: effective_from (TIMESTAMP NOT NULL), effective_to with default '9999-12-31', and is_current BOOLEAN with default true"
},
{
"name": "SCD Type 2 on product dim",
"max_score": 10,
"description": "The product dimension includes all three SCD Type 2 fields: effective_from (TIMESTAMP NOT NULL), effective_to with default '9999-12-31', and is_current BOOLEAN with default true"
},
{
"name": "Fact grain: line item",
"max_score": 7,
"description": "The order fact table is documented or named to indicate the grain is one row per order line item (not per order)"
},
{
"name": "Derived measure: gross_revenue",
"max_score": 8,
"description": "The fact table defines gross_revenue as quantity multiplied by unit_price (comment or column name makes this derivation clear)"
},
{
"name": "Derived measure: net_revenue",
"max_score": 8,
"description": "The fact table defines net_revenue as gross_revenue minus discount_amount"
},
{
"name": "Derived measure: gross_profit",
"max_score": 8,
"description": "The fact table defines gross_profit as net_revenue minus cost_of_goods"
},
{
"name": "is_first_order flag",
"max_score": 7,
"description": "The order line item fact table includes an is_first_order BOOLEAN column"
},
{
"name": "Daily aggregate fact table",
"max_score": 8,
"description": "A separate daily aggregate fact table is defined for dashboard use (distinct from the line-item fact table)"
},
{
"name": "Aggregate fact composite PK",
"max_score": 7,
"description": "The daily aggregate fact table has a composite primary key on (date_key, channel_key)"
},
{
"name": "Date dimension attributes",
"max_score": 8,
"description": "The date dimension includes fiscal period fields (fiscal_quarter or fiscal_year) and at least one of: is_holiday, is_weekend, week_of_year"
},
{
"name": "Monetary convention documented",
"max_score": 11,
"description": "The schema file or design-notes.md explicitly states whether monetary values are stored as cents (integers) or dollars (decimals) and applies that convention consistently"
}
]
}
Analytics Warehouse Schema for GreenLeaf Commerce
Problem/Feature Description
GreenLeaf Commerce is a mid-sized online retailer that sells organic gardening products. Their engineering team has been storing all transactional data in an operational PostgreSQL database, and the data analysts are struggling to run reporting queries directly on it — they're causing performance issues and the schema isn't optimized for aggregations.
The analytics team has decided to build a dedicated data warehouse. They need SQL DDL definitions for a star schema that will power their reporting dashboards. The warehouse needs to handle order analysis, product performance, customer segmentation, and time-based trending. They want the schema to support historical tracking of product price changes and customer segment changes over time, so reports can reflect what was true at the time of each transaction rather than current values.
The team also needs a separate aggregate table that their executive dashboards can query quickly without scanning the full order line detail. Their BI tool expects a consistent monetary format, so they want all money fields documented with a clear convention.
Output Specification
Produce a single SQL file schema.sql containing:
- All dimension table definitions (at minimum: date, customer, product, and channel dimensions)
- All fact table definitions (at minimum: a line-item fact and a daily aggregate fact)
- Comments explaining the grain of each fact table and the monetary value convention used
Also produce a short design-notes.md file explaining key design decisions made, including how historical changes are handled and how dates are keyed.
{
"name": "finsi/ecommerce-data-warehouse",
"version": "0.1.0",
"summary": "Data warehouse design for commerce — star schema, ETL pipelines, dbt models",
"skills": {
"ecommerce-data-warehouse": {
"path": "SKILL.md"
}
}
}