Why it matters

The raw Data Vault records history: satellites are insert-only, and every row carries the timestamp of when the warehouse loaded it. That is the right shape for storage, but a poor shape for reading. A customer timeline exists only as a pile of version rows, and an as-of question has to re-find the row that was effective at that moment every time it is asked.

The Business Vault is the interpretation layer. It turns satellite history into consumption-ready tables — effective-dated SCD2 timelines, technical as-of PIT snapshots, bridges — and most of that work is generated from the model rather than written by hand. Get this layer right and every mart above it inherits clean history and stable keys; skip it and each report re-implements effective-dating logic on its own.

Prerequisites: the local database is up (./scripts/run-postgres.sh up) and the retail warehouse has been loaded once — ./scripts/run-hop.sh retail-example workflows/run-retail-initial.hwf. Budget 30-40 minutes. The walkthrough works on retail-example/ and repeats the same steps on your own models.

Concepts

The interpretation layer

A Business Vault model is an .hbv file that references a linked Data Vault model (.hdv). It reads hub and satellite history from the Data Vault target database and writes its tables to the Business Vault target database. The two connections are configured separately (the DV one on the .hdv, the BV one on the .hbv) and may or may not be the same; both must resolve in the Hop project, and Check model validates that before any pipeline is generated.

Choose the table type for the question

Table type The question it answers

SCD2 (BvScd2Table)

"What did this customer look like at business time T?" — one functional timeline merged from one or more satellites, with validity bounds.

PIT (BvPitTable)

"Which satellite row was effective at technical time T?" — one row per hub key and snapshot date, with a load-timestamp pointer per satellite.

Bridge (BvBridge)

"Which customers bought which products?" — two or more hub hash keys, usually taken from a Data Vault link, flattened for reporting joins. Optional weight.

SQL view / table (BvBusinessTable)

"Anything else we can express in SQL" — business rules, enrichments and lookups you author on the canvas, materialized as a view or a table with ref() / source() dependencies.

Source query (BvSourceQuery)

A satellite-shaped input that lives outside the Data Vault target (a corrective view on another database, for example). SCD2 and PIT can read it; Business Vault Update does not load it.

Identity map (BvIdentityMap)

"Are these two records the same customer?" — a shared raw-hub-hash to durable-hash map for one hub.

Source calendar (BvSourceCalendar)

"When may each source system contribute rows?" — shared cutover windows. Metadata only: nothing is created or loaded for it.

Data Vault hubs, links and satellites are referenced on a .hbv canvas as Linked Hub, Linked Link and Linked Satellite cards, so a table names the vault objects it reads.

SCD2: functional timelines

An SCD2 table merges satellite history into version rows. Every functional change opens a version with validity bounds (valid_from / valid_to by default; the retail project’s configuration uses x_from_ts / x_to_ts). The functional timestamp decides where a version starts — the moment the source system said the value became true. It resolves in this order: per-satellite override, then the per-table field, then the model default, then the load-date fallback. The retail 360 model sets it to x_load_ts on the table.

An SCD2 table is pure Type 2: a value change becomes a new version row and the older rows stay queryable. There is deliberately no per-column Type 1 switch that would rewrite historical versions when a "current" value changes — mixing the two would make some columns look historical while they are not. When users need a value as it is now (say, the current sales rep on old facts), resolve it downstream: join the open SCD2 row, read the latest satellite row, use a separate current-state view or table, or use hybrid Type 1 + Type 2 in the dimensional model.

Field mappings are required when two or more inputs feed one table, and Suggest mappings fills the unmapped attributes of the satellites and source queries you select. On Satellite settings, each satellite can override the functional timestamp, carry a source indicator value (the merge tag used when several satellites are merged), and name the calendar system it belongs to when a source calendar is selected.

PIT: technical as-of snapshots

A PIT table materializes the as-of lookup once, so consumers join instead of re-deriving it: one row per (hub hash key, snapshot date), plus one pointer column per satellite (default suffix _ldts) holding the load timestamp of the satellite row that was effective at that snapshot. A query then joins sat.x_load_ts = pit.sat_customer_ldts for the snapshot it wants.

PIT reads raw hub and satellite history — not SCD2 output. SCD2 and PIT are independent siblings in the same run, and SCD2 tables run first. The snapshot spine is a schedule: Snapshot cadence (daily only today; the other cadences validate but cannot be generated yet), Snapshot anchor (end of period by default), Horizon days (default 1 — skips the most recent days as a late-arriving-data buffer), and Range start / Range end (default: the earliest participating satellite load to now minus the horizon). A PIT table that already exists only receives snapshot dates newer than its highest snapshot_date.

PIT reads raw hub and satellite history; SCD2 is an independent sibling

Identity maps and survivorship

An identity map resolves many raw hub hashes to one durable hash. Add it with Add identity map (a parent hub and a same-as link are what it needs), and an SCD2 table opts in with Identity map; leave that blank and the timeline keeps the raw hub hash as its grain. The generated map upserts and never truncates: a raw key that already has a durable key keeps it, so hk_durable is frozen on first assignment and survives the next run. Rows that predate the mapping follow the unmapped policy — self keeps the raw hash, quarantine and drop leave the row out of the timeline.

Identity map — freezing the durable hash on first assignment

Survivorship decides values when several sources feed the same target column. Give each of those mapping rows a Rank — 1 wins over 2 — and the merge keeps the best-ranked value at every timestamp. Null policies decide what a null does (inherit skips to the next rank, apply writes it). A table with no ranks keeps the normal merge behaviour.

Source calendars

A source calendar is shared metadata — not a warehouse table — that says when each source system may contribute rows. Each entry is a half-open window: a calendar system name you choose (ORACLE, KAFKA, …), an effective-from and an effective-to, where an empty effective-to means the source is still in use. An SCD2 table opts in with Source calendar and then names the calendar system per satellite on Satellite settings; leave it blank and every satellite row is kept. The documented example: Oracle rows count through 2024-02-29, Kafka rows from 2024-03-01.

How a Business Vault load runs

Business Vault Update (BUSINESS_VAULT_UPDATE) mirrors Data Vault Update: model check, optional DDL on the target, pipeline generation, staging, parallel execution, and optional catalog publish. Within one run the order is fixed: SCD2 tables, then PIT, then bridges, then SQL business tables. Inside a full warehouse wave, Update resource definition group runs all Data Vault models first, then all Business Vault models, then all dimensional models — each layer in list order.

Debug (toolbar) and Show build pipeline (table context menu) generate a build pipeline and open it without writing any data. That is the fastest way to read what a table will actually do before a load touches the warehouse.

Guided walkthrough

Read the finished Business Vault

  1. Open the project folder retail-example/ in Hop GUI and open models/retail-360.hbv (or select it under 4. Business Vault in the EDW Journey perspective).

  2. Read its tables: customer_360_bv is one SCD2 table that merges four satellites — sat_customer_demo, sat_customer_contact, sat_customer_address, sat_customer_prefs — around hub_customer. The model also carries a source calendar (Oracle-Kafka-Calendar) and a SQL calculation (online_indicator). The group’s Business Vault tab also lists retail-sql.hbv, which materializes satb_product_hb, a historical view over sat_product.

    Business Vault — the retail Customer 360 SCD2 table
  3. Open the table dialog (double-click customer_360_bv) and walk its tabs: General (physical table name, Include hash key, Include hub business keys, Build mode, validity fields), Derivatives (the hub and the four satellites), Field mappings, Satellite settings and Calculations.

  4. Note the interaction model: the canvas uses left-click context menus (there is no right-click menu), and those menus are where you add your own tables.

Build your own SCD2 table

  1. In your own project, open (or create) a .hbv and set Edit model → Data Vault model file to your .hdv. On the canvas, place the vault cards you need: Add Linked Hub for the hub, then Add Linked Satellite for each satellite that feeds the table.

  2. Add SCD2 table, then set Parent hub in the dialog and pick the satellites on the Derivatives tab. On Field mappings, press Suggest mappings and select the satellites to fill; keep, rename or drop the proposed target columns.

    SCD2 table dialog — General tab
    SCD2 table dialog — Derivatives tab
  3. Read the Field mappings list: each row maps one satellite attribute to one Business Vault column, and every mapped attribute is history (Type 2). Pick one attribute you would want to be always current — a loyalty tier, a current sales rep — and note that the plugin keeps SCD2 pure Type 2, so that value will be resolved downstream instead.

    SCD2 table dialog — Field mappings
  4. Use Show build pipeline on the table to read the generated pipeline: satellite reads, the sorted merge, the LAG / LEAD validity computation, and the load into the target. Nothing is written by opening it.

  5. Open Satellite settings and read the per-satellite overrides: functional timestamp, source indicator value, and the calendar system used when a source calendar is selected on the table.

    SCD2 table dialog — Satellite settings
  6. Add a PIT table over the same hub: Add PIT table, set exactly one hub and at least one satellite as derivatives, and read the snapshot schedule — Snapshot cadence, Snapshot anchor, Horizon days, Range start and Range end.

  7. Check model until it reports no errors.

Register and load the model

  1. Add the .hbv to your warehouse: Metadata → EDW → Resource definition group, open your group, add the file on the Business Vault models tab and save.

  2. Run Update resource definition group again. The action loads all Data Vault models first, then all Business Vault models, then all dimensional models, each layer in list order; watch the log for the SCD2 and PIT pipelines.

  3. Prove it in the database. In the retail example the SCD2 table is customer_360_bv; use the name your own model generated if you followed the steps on your own vault. In the Database perspective, connect Vault, expand Vault → public, select the table and press Show object information:

    Expected in the column list: the parent hash key, the hub business keys, the validity columns (x_from_ts / x_to_ts in this project), and one column per field mapping. Preview table shows the rows themselves.

Try it yourself

Take one attribute your SCD2 table will carry that users will always want as it is now — a loyalty tier, a current sales rep — and decide its fate:

  1. Map the attributes that are history. Every mapped column is Type 2 by design; there is nothing to mark.

  2. Write one line: where will the current value come from? (Join the open SCD2 row; read the latest satellite row; use a separate current-state view or table; or keep the rule for hybrid Type 1 + Type 2 in the dimensional model.)

  3. Run Check model and fix whatever it reports.

Check your work: no model check errors, and your line matches the Type 1 vs Type 2 rule in Concepts — history stays clean Type 2 on the SCD2 table and "current" is resolved downstream. If your note says "switch the column to Type 1 on the SCD2 table", re-read that section: the plugin deliberately offers no such switch.

Questionnaire

Answer the eight questions. Every answer comes with its reasoning, and Copy my results gives you a text summary to keep.

1. What does the Business Vault add over the raw Data Vault?

2. SCD2 table or SQL view / table — how do you choose?

3. What does a PIT table solve?

4. Which statement about Type 1 and Type 2 is correct for a Business Vault SCD2 table?

5. What does an identity map freeze?

6. Scenario: two source systems disagree about a customer's name, and both changes arrive in satellites that one SCD2 table merges. Which Business Vault feature decides the value?

7. In which order does the Update resource definition group action load the warehouse models?

8. What is a source calendar?

Where to go next