This site is under active development — some features may be incomplete
Back to all articles
2026-02-19 14 min read

Data Vault 2.0 in Practice: Lessons from Real Implementations

What Data Vault actually looks like in production — and when to use it

Data Vault 2.0 is one of those methodologies that sounds elegant in theory but reveals its true nature only when you build and operate it at scale. After implementing Data Vault across several enterprise data platforms — from financial services to e-commerce — I have strong opinions about when it shines, when it struggles, and what the books and courses leave out.

This is not a tutorial. If you want the textbook definitions, read Dan Linstedt's books. This is the practitioner's guide — the patterns, pitfalls, and hard decisions you face when you commit to Data Vault in a real organization with real deadlines, real source systems, and real analysts who need to query the results.

What Data Vault Actually Is (in 60 Seconds)

Data Vault is a modeling methodology for the integration layer of a data warehouse — the Raw Vault. It sits between your staging area and your presentation layer (where you build dimensional models, wide tables, or whatever your consumers need). The core idea is deceptively simple: separate structure from context from relationships.

The methodology uses three primary entity types:

  • Hubs — represent core business concepts (Customer, Order, Product). They store only the business key and metadata (load date, record source). A hub is the anchor point for a business entity.
  • Links — represent relationships or transactions between hubs (Customer_Order, Order_Product). They store the foreign keys to the related hubs plus metadata. Links capture the fact that a relationship exists.
  • Satellites — store the descriptive attributes (context) that change over time. A satellite hangs off a hub or link and captures every change with full history. Customer_Detail_Sat might hold name, email, address. Order_Detail_Sat holds amounts, status, timestamps.
Data Vault 2.0 Core Structure: Hubs, Links, and Satellites
HUB_CUSTOMER customer_hk (hash key) customer_bk | load_date | src HUB_ORDER order_hk (hash key) order_bk | load_date | src LNK_CUST_ORDER link_hk | customer_hk | order_hk load_date | record_source SAT_CUSTOMER_DETAIL customer_hk | load_date name | email | phone hash_diff | record_source SAT_CUSTOMER_ADDR customer_hk | load_date street | city | state | zip hash_diff | record_source SAT_ORDER_DETAIL order_hk | load_date amount | status | currency hash_diff | record_source ESAT_CUST_ORDER link_hk | load_date effective_from | effective_to Hubs (business keys) Links (relationships) Satellites (context) Structure (Hubs) + Relationships (Links) + Context (Satellites) = Full History

The separation is the key insight. When a new source system comes online, you add new satellites — you don't restructure your hubs or links. When a relationship changes, you update the link satellite without touching the descriptive data. This insert-only, additive architecture is what gives Data Vault its resilience to change.

Hash Keys: The Foundation You Cannot Get Wrong

Data Vault 2.0 introduced hash keys as a replacement for sequence-based surrogate keys. Every hub, link, and satellite uses a hash of the business key(s) as its primary key. This is one of the most important — and most debated — decisions in the methodology.

Why hashes? Because they enable parallel, idempotent loading. When you hash the business key, any process can independently compute the same key without consulting a lookup table or sequence generator. This means you can load hubs, links, and satellites in parallel from different source systems, and they will naturally align.

-- Hash key generation in dbt (using MD5 for illustration; use SHA-256 in production)
-- Hub Customer
SELECT
    MD5(UPPER(TRIM(customer_id))) AS customer_hk,
    customer_id AS customer_bk,
    CURRENT_TIMESTAMP AS load_date,
    'CRM_SYSTEM' AS record_source
FROM staging.crm_customers
WHERE customer_id IS NOT NULL

-- Link Customer_Order
SELECT
    MD5(CONCAT(
        MD5(UPPER(TRIM(customer_id))),
        MD5(UPPER(TRIM(order_id)))
    )) AS link_hk,
    MD5(UPPER(TRIM(customer_id))) AS customer_hk,
    MD5(UPPER(TRIM(order_id))) AS order_hk,
    CURRENT_TIMESTAMP AS load_date,
    'ORDER_SYSTEM' AS record_source
FROM staging.orders

The lessons I have learned about hash keys the hard way:

  • Standardize your hashing function organization-wide. If one team uses MD5 and another uses SHA-256, or one trims and uppercases while the other does not, your keys will never match. Write a shared macro and enforce it.
  • Always UPPER and TRIM business keys before hashing. Source systems are inconsistent. 'ABC-123' and ' abc-123 ' must produce the same hash. I have seen entire loads fail because of trailing spaces.
  • Use SHA-256 in production, not MD5. MD5 collision probability is negligible at most data volumes, but SHA-256 removes the argument entirely. The performance difference on modern hardware is minimal.
  • Hash diffs for satellites are non-negotiable. A hash_diff column (hash of all descriptive columns) in every satellite lets you detect changes without comparing every column. This is the single biggest performance optimization in satellite loading.

Data Vault vs. Dimensional Modeling: The Real Trade-offs

This is the question I get asked most often: "Should we use Data Vault or Kimball dimensional modeling?" The honest answer is nuanced, and anyone who gives you an absolute answer for all situations is selling something.

Data Vault vs. Dimensional Modeling: Architecture Comparison
DATA VAULT APPROACH CRM ERP Web Events Staging (as-is from source) RAW VAULT Hubs Links Satellites Insert-only | Full history | Auditable Business Vault (derived, computed) Information Marts (Star Schemas) BI tools / Analysts / Reports DIMENSIONAL APPROACH CRM ERP Web Events Staging (as-is from source) Transform STAR SCHEMA fct_orders dim_customer dim_product dim_date Directly queryable | Optimized for BI BI tools / Analysts / Reports More layers, more flexibility Fewer layers, faster to query

Choose Data Vault when:

  • You have many source systems feeding the same business concepts. If "customer" comes from CRM, ERP, web analytics, and a mobile app, Data Vault's hub-and-satellite pattern handles integration gracefully. Each source gets its own satellite(s), and the hub provides a single anchor point.
  • Auditability is non-negotiable. Financial services, healthcare, government — anywhere regulators might ask "show me every version of this record and where it came from." Data Vault's insert-only approach with record_source and load_date on every row gives you this by default.
  • Requirements change frequently. When your business analysts change their minds every quarter about what metrics matter, the Raw Vault does not care. You add or modify satellites and rebuild the presentation layer — the core model stays stable.
  • Your team has the discipline. Data Vault requires rigor. Naming conventions, hashing standards, loading patterns — they all need to be consistent. Without disciplined engineering, the vault becomes a chaotic mess of poorly named tables.

Choose dimensional modeling (Kimball) when:

  • You have a small team and need to deliver fast. A star schema with a few fact tables and dimensions is dramatically simpler to build, query, and explain. Data Vault adds layers of complexity that may not be justified.
  • Your source landscape is simple. If you have one or two source systems and stable schemas, the integration benefits of Data Vault do not justify the overhead.
  • Performance for end users is the top priority. Star schemas are optimized for BI tools. Data Vault queries (especially without PIT and bridge tables) require multiple joins and are inherently slower for analytical queries.
  • You do not need full history of every change. SCD Type 2 in dimensional models handles slowly changing dimensions for the cases where it matters. Data Vault captures everything, whether you need it or not.

In practice, the best implementations use both. Data Vault as the integration and historization layer, with dimensional models built on top as the presentation layer. This is the pattern I recommend for organizations with any real complexity.

Loading Patterns That Actually Work

The loading strategy is where theory meets reality. Data Vault loading has a specific sequence that must be respected, but within that sequence, there are patterns that make the difference between a pipeline that runs in 20 minutes and one that takes 4 hours.

The Loading Order

The fundamental rule: Hubs first, then Links, then Satellites. Links reference hubs, so hub keys must exist before link records can be inserted. Satellites reference hubs or links, so those parent records must exist first. Within each type, loads can run in parallel.

# dbt Data Vault loading order with tags
# In dbt_project.yml:
models:
  raw_vault:
    hubs:
      +tags: ['hub']
      +materialized: incremental
      +incremental_strategy: merge
      +unique_key: 'hub_hash_key'
    links:
      +tags: ['link']
      +materialized: incremental
      +incremental_strategy: merge
      +unique_key: 'link_hash_key'
    satellites:
      +tags: ['satellite']
      +materialized: incremental
      +incremental_strategy: append  # Insert-only for satellites!
      +unique_key: ['hash_key', 'load_date']

Satellite Loading: The Delta Detection Pattern

Satellite loading is where most performance problems live. The key pattern is delta detection using hash diffs: only insert a new satellite record when the descriptive attributes have actually changed.

-- Satellite loading pattern (dbt incremental model)
-- SAT_CUSTOMER_DETAIL

WITH source AS (
    SELECT
        MD5(UPPER(TRIM(customer_id))) AS customer_hk,
        CURRENT_TIMESTAMP AS load_date,
        'CRM' AS record_source,
        customer_name,
        email,
        phone,
        MD5(CONCAT_WS('||',
            COALESCE(UPPER(TRIM(customer_name)), ''),
            COALESCE(UPPER(TRIM(email)), ''),
            COALESCE(UPPER(TRIM(phone)), '')
        )) AS hash_diff
    FROM {{ ref('stg_crm_customers') }}
),

{% if is_incremental() %}
latest_satellite AS (
    SELECT customer_hk, hash_diff
    FROM (
        SELECT
            customer_hk,
            hash_diff,
            ROW_NUMBER() OVER (
                PARTITION BY customer_hk
                ORDER BY load_date DESC
            ) AS rn
        FROM {{ this }}
    ) ranked
    WHERE rn = 1
)
{% endif %}

SELECT
    s.customer_hk,
    s.load_date,
    s.record_source,
    s.customer_name,
    s.email,
    s.phone,
    s.hash_diff
FROM source s
{% if is_incremental() %}
LEFT JOIN latest_satellite ls
    ON s.customer_hk = ls.customer_hk
WHERE ls.customer_hk IS NULL  -- New hub key (first-time load)
   OR ls.hash_diff != s.hash_diff  -- Attribute changed
{% endif %}

This pattern is critical. Without delta detection, you insert a new satellite row every time the pipeline runs, even if nothing changed. On large tables, this leads to massive satellite bloat and destroys query performance.

Multi-Source Hub Loading

When multiple sources contribute to the same hub, the loading pattern must handle deduplication at the hub level while preserving all source-specific records in satellites:

-- Hub loading from multiple sources
WITH all_customers AS (
    SELECT
        MD5(UPPER(TRIM(customer_id))) AS customer_hk,
        customer_id AS customer_bk,
        'CRM' AS record_source,
        load_timestamp
    FROM {{ ref('stg_crm_customers') }}

    UNION ALL

    SELECT
        MD5(UPPER(TRIM(customer_id))) AS customer_hk,
        customer_id AS customer_bk,
        'WEB_APP' AS record_source,
        load_timestamp
    FROM {{ ref('stg_web_customers') }}
),

-- Keep the earliest record source for each business key
ranked AS (
    SELECT *,
        ROW_NUMBER() OVER (
            PARTITION BY customer_hk
            ORDER BY load_timestamp ASC
        ) AS rn
    FROM all_customers
)

SELECT
    customer_hk,
    customer_bk,
    record_source,
    load_timestamp AS load_date
FROM ranked
WHERE rn = 1
{% if is_incremental() %}
    AND customer_hk NOT IN (SELECT customer_hk FROM {{ this }})
{% endif %}

PIT Tables and Bridge Tables: Making Data Vault Queryable

Here is the dirty secret of Data Vault: the Raw Vault is terrible to query directly. A simple "give me the current state of all customers with their orders" requires joining a hub to multiple satellites (each needing a subquery or window function to get the latest record), then through a link to another hub and its satellites. You end up with six, eight, sometimes twelve joins.

This is where Point-In-Time (PIT) tables and Bridge tables become essential.

Point-In-Time (PIT) Tables

A PIT table pre-computes the "as-of" join between a hub and all its satellites for a given point in time. Instead of each query recalculating which satellite record was active at which time, the PIT table has already done that work:

-- PIT table structure for Customer
-- Pre-computes: for each customer at each snapshot, which satellite records are current?

CREATE TABLE pit_customer AS
SELECT
    c.customer_hk,
    snap.snap_date,
    -- Latest SAT_CUSTOMER_DETAIL as of snap_date
    (
        SELECT MAX(load_date)
        FROM sat_customer_detail sd
        WHERE sd.customer_hk = c.customer_hk
          AND sd.load_date <= snap.snap_date
    ) AS sat_customer_detail_load_date,
    -- Latest SAT_CUSTOMER_ADDRESS as of snap_date
    (
        SELECT MAX(load_date)
        FROM sat_customer_addr sa
        WHERE sa.customer_hk = c.customer_hk
          AND sa.load_date <= snap.snap_date
    ) AS sat_customer_addr_load_date
FROM hub_customer c
CROSS JOIN snapshot_dates snap;

-- Now a query that previously needed complex window functions becomes:
SELECT
    h.customer_bk,
    sd.customer_name,
    sd.email,
    sa.city,
    sa.state
FROM pit_customer pit
JOIN hub_customer h ON pit.customer_hk = h.customer_hk
JOIN sat_customer_detail sd
    ON pit.customer_hk = sd.customer_hk
   AND pit.sat_customer_detail_load_date = sd.load_date
JOIN sat_customer_addr sa
    ON pit.customer_hk = sa.customer_hk
   AND pit.sat_customer_addr_load_date = sa.load_date
WHERE pit.snap_date = CURRENT_DATE;

Bridge Tables

Bridge tables pre-resolve the joins across links. They flatten the hub-link-hub traversal into a single table with all the relevant hash keys, making multi-hop joins performant:

-- Bridge table: Customer Orders
-- Pre-resolves the Hub -> Link -> Hub join path
CREATE TABLE bridge_customer_orders AS
SELECT
    hc.customer_hk,
    hc.customer_bk,
    lnk.link_hk,
    ho.order_hk,
    ho.order_bk,
    lnk.load_date AS link_load_date
FROM hub_customer hc
JOIN lnk_customer_order lnk ON hc.customer_hk = lnk.customer_hk
JOIN hub_order ho ON lnk.order_hk = ho.order_hk;

PIT and bridge tables are not optional in production. Without them, your analysts will revolt and your BI tools will time out. Budget for them in your estimates — they typically add 30-50% more models to your dbt project.

Query Complexity: Raw Vault vs. PIT/Bridge Tables
WITHOUT PIT/BRIDGE SELECT customer, orders, details... HUB_CUST JOIN SAT_DETAIL + WINDOW SAT_ADDR + WINDOW LNK_C_ORD JOIN HUB_ORDER JOIN SAT_ORD_DET + WINDOW ESAT_C_ORD + WINDOW 7 joins + 3 window functions = slow WITH PIT/BRIDGE SELECT customer, orders, details... PIT_CUSTOMER Pre-resolved sats BRIDGE_C_ORD Pre-resolved joins SAT_DETAIL SAT_ADDR SAT_ORD_DET 4 simple joins, no window functions Production rule: Never expose Raw Vault to analysts. Always build PIT tables, Bridge tables, or Information Marts on top.

The Business Vault: Where Derived Logic Lives

The Raw Vault stores data as received from source systems — no business rules, no transformations, no interpretations. But business logic needs to live somewhere. That is the Business Vault.

The Business Vault contains computed satellites, same-as links, and other derived entities. The distinction is philosophical but important: everything in the Raw Vault is reproducible from the source data. Everything in the Business Vault encodes a business decision.

Common Business Vault patterns I use regularly:

  • Computed satellites — derived metrics attached to hubs. A BSAT_CUSTOMER_LIFETIME_VALUE that computes CLV from order satellites and attaches it to the customer hub.
  • Same-as links — when two source systems refer to the same entity with different business keys. SAL_CUSTOMER_CRM_WEB links the CRM customer hub key to the web app customer hub key when you have determined they are the same person.
  • Computed links — derived relationships that do not exist in any source system but are inferred through business rules.
  • Reference tables — shared lookup data (country codes, currency codes) that are used across the vault.
-- Business Vault: Computed Satellite for Customer Lifetime Value
-- This encodes a business rule (CLV calculation) and hangs off HUB_CUSTOMER

WITH order_totals AS (
    SELECT
        bridge.customer_hk,
        SUM(sod.order_amount) AS total_spend,
        COUNT(DISTINCT bridge.order_hk) AS order_count,
        MIN(sod.order_date) AS first_order_date,
        MAX(sod.order_date) AS last_order_date
    FROM {{ ref('bridge_customer_orders') }} bridge
    JOIN {{ ref('sat_order_detail') }} sod
        ON bridge.order_hk = sod.order_hk
    -- Get latest satellite record per order
    QUALIFY ROW_NUMBER() OVER (
        PARTITION BY bridge.order_hk
        ORDER BY sod.load_date DESC
    ) = 1
    GROUP BY bridge.customer_hk
)

SELECT
    customer_hk,
    CURRENT_TIMESTAMP AS load_date,
    'BUSINESS_VAULT' AS record_source,
    total_spend,
    order_count,
    first_order_date,
    last_order_date,
    DATEDIFF('day', first_order_date, last_order_date) AS customer_tenure_days,
    total_spend / NULLIF(order_count, 0) AS avg_order_value,
    MD5(CONCAT_WS('||',
        COALESCE(CAST(total_spend AS VARCHAR), ''),
        COALESCE(CAST(order_count AS VARCHAR), ''),
        COALESCE(CAST(first_order_date AS VARCHAR), ''),
        COALESCE(CAST(last_order_date AS VARCHAR), '')
    )) AS hash_diff
FROM order_totals

Pitfalls and Lessons Learned

After several Data Vault implementations, these are the patterns and anti-patterns I have seen repeatedly. Some of these took expensive mistakes to learn.

1. The "Too Many Satellites" Problem

The methodology says to split satellites by rate of change, source system, and data sensitivity. In theory, this is sound. In practice, I have seen implementations with 15+ satellites per hub because teams split by every possible dimension. The result: every query becomes a nightmare of joins, PIT tables become enormous, and load times explode.

My rule: Start with one satellite per hub per source system. Split further only when you have a concrete performance or governance reason. If your customer hub has satellites for demographics, contact info, preferences, scores, flags, and metadata — and they all change at the same rate from the same source — collapse them into one satellite. Purity is not worth the operational cost.

2. Effectivity Satellites Are Not Optional

An effectivity satellite (ESAT) tracks when a relationship in a link becomes active or inactive. Without it, your link table says "Customer A was related to Order B" but never says "this relationship ended" or "this assignment changed."

I have seen teams skip ESATs to simplify the model, then discover months later that their reports include cancelled orders, terminated contracts, and reassigned accounts. Effectivity satellites are essential for any link that represents a relationship that can end.

3. Ghost Records and the Unknown Problem

What happens when a foreign key in a source record references a hub key that does not exist yet? In traditional dimensional modeling, you use a "late-arriving dimension" pattern. In Data Vault, you have two choices: reject the link record (and risk losing it) or insert a "ghost record" into the hub as a placeholder.

Ghost records work, but you need a systematic way to detect and resolve them. I use a pattern where ghost records get a specific record_source value ('GHOST') and a scheduled job checks for ghosts that have been resolved by actual source data, updating the record_source on the ghost.

4. The Naming Convention is Everything

Data Vault generates a lot of tables. A moderately complex implementation might have 50-100 hubs, 100-200 links, and 300-600 satellites, plus PIT tables, bridge tables, and business vault objects. Without strict naming conventions, it becomes unnavigable.

The naming convention I enforce:

-- Naming convention that scales
HUB_CUSTOMER              -- Hubs: HUB_{business_concept}
HUB_ORDER
HUB_PRODUCT

LNK_CUSTOMER_ORDER        -- Links: LNK_{hub1}_{hub2}
LNK_ORDER_PRODUCT
LNK_ORDER_SHIPMENT

SAT_CUSTOMER_DETAIL_CRM   -- Satellites: SAT_{hub/link}_{descriptor}_{source}
SAT_CUSTOMER_DETAIL_WEB
SAT_CUSTOMER_ADDR_CRM
SAT_ORDER_DETAIL_ERP
SAT_ORDER_STATUS_OMS

ESAT_CUSTOMER_ORDER       -- Effectivity Sats: ESAT_{link_name}
BSAT_CUSTOMER_CLV         -- Business Sats: BSAT_{hub}_{descriptor}
SAL_CUSTOMER_CRM_WEB      -- Same-As Links: SAL_{concept}_{src1}_{src2}

PIT_CUSTOMER              -- PIT Tables: PIT_{hub}
PIT_ORDER
BRG_CUSTOMER_ORDERS       -- Bridge Tables: BRG_{traversal_name}

5. Underestimating the Presentation Layer

This is the mistake I see most often. Teams invest months building a pristine Raw Vault and Business Vault, then rush the presentation layer. But the presentation layer is what your stakeholders actually interact with. A well-built Raw Vault that feeds terrible dashboards is a failure.

Budget at least 40% of your development effort for the presentation layer — dimensional models, wide denormalized tables, aggregations, and the views/APIs that serve them. This is where you convert the flexibility of the vault into the usability of a well-designed star schema.

6. Hash Collisions Are Real (Sort Of)

With MD5, the probability of a collision is roughly 1 in 2^64 for a birthday collision. At 1 billion records, this is about 1 in 18 billion. Practically zero, but "practically zero" does not satisfy auditors in regulated industries. SHA-256 eliminates this conversation entirely at minimal performance cost. I always use SHA-256 in production and reserve MD5 for development and testing where load speed matters more.

7. AutomateDV and dbtvault: Use the Frameworks

Do not hand-write every hub, link, and satellite model. The dbt ecosystem has mature frameworks that generate Data Vault models from metadata definitions:

-- Using AutomateDV (formerly dbtvault) in dbt
-- Define your vault structure in YAML, generate models automatically

{%- set source_model = "stg_crm_customers" -%}
{%- set src_pk = "customer_hk" -%}
{%- set src_nk = "customer_bk" -%}
{%- set src_ldts = "load_date" -%}
{%- set src_source = "record_source" -%}

{{ automate_dv.hub(
    src_pk=src_pk,
    src_nk=src_nk,
    src_ldts=src_ldts,
    src_source=src_source,
    source_model=source_model
) }}

These frameworks handle the repetitive boilerplate — hash key generation, delta detection, satellite loading patterns — and let you focus on the modeling decisions that actually require thought. I have seen teams cut their development time by 50-60% by adopting AutomateDV early.

Data Vault on Modern Infrastructure

Data Vault was designed in an era of on-premise data warehouses. On modern cloud infrastructure — Snowflake, BigQuery, Databricks, Redshift — some of the original constraints no longer apply, and new opportunities emerge:

  • Snowflake's zero-copy cloning makes PIT table refresh trivial — clone the table, truncate, and rebuild rather than incremental updates. Time Travel also provides an alternative to some historization patterns.
  • BigQuery's partitioned tables can partition satellites by load_date, making delta detection queries dramatically faster since they only scan the latest partition.
  • Snowflake's MERGE statement simplifies hub and link loading by handling INSERT-or-SKIP logic in a single operation.
  • Cloud storage costs are low enough that the "store everything, decide later" philosophy of Data Vault becomes economically viable even at petabyte scale.
  • Apache Iceberg and Delta Lake with time travel capabilities overlap with some of the historization that Data Vault provides. On a lakehouse, you may not need satellite-level change tracking for every entity — the table format itself provides it.

The question I ask every team: "Do you need change tracking at the record level, or is table-level versioning (via Iceberg/Delta snapshots) sufficient?" If the answer is table-level, you can simplify your vault significantly.

When to Walk Away

Data Vault is not the right answer for every situation. I recommend against it when:

  • Your team is small (fewer than 3 data engineers). The operational overhead of maintaining a full vault — staging, Raw Vault, Business Vault, PIT tables, bridge tables, presentation layer — is substantial. A small team is better served by a well-designed star schema with SCD Type 2 where needed.
  • Your source systems are stable and few. Data Vault's integration benefits shine when you have 5, 10, 20 source systems feeding overlapping business concepts. With one or two stable sources, the complexity is not justified.
  • Speed to insight is the top priority. If the business needs dashboards next week, not next quarter, build a dimensional model. You can always refactor to Data Vault later (though I will warn you: that refactoring is painful).
  • Your organization lacks data modeling maturity. Data Vault requires discipline — naming conventions, loading patterns, governance. If your team does not yet have strong data modeling practices, Data Vault will amplify the chaos rather than contain it.

Conclusion

Data Vault 2.0 is a powerful methodology for building enterprise-grade data integration layers. It delivers on its promises of auditability, parallel loading, and resilience to change — but only when implemented with rigor and pragmatism. The Raw Vault must be complemented by PIT tables, bridge tables, and a well-designed presentation layer. The Business Vault must have clear rules about what constitutes a derived entity versus a source entity. And the entire structure must be supported by automation, whether through AutomateDV, custom dbt macros, or metadata-driven generation.

The teams I have seen succeed with Data Vault share three traits: they have strong naming and governance conventions established before they write a single model, they budget for the presentation layer from day one, and they are pragmatic about splitting satellites. The methodology is a tool, not a religion. Use it where it fits, adapt it where it does not, and never lose sight of the fact that the value of a data warehouse is measured by the questions it can answer — not by the purity of its integration layer.