Перейти к содержанию

Data Vault 2.0: как собрать хранилище из конструктора

📚 Оглавление

  1. Зачем вообще нужен Data Vault?
  2. Интуиция: DV как конструктор Lego
  3. Три типа таблиц в Data Vault 2.0
  4. Типы сателлитов в DV 2.0
  5. Raw Vault и Business Vault
  6. Пример: клиент и заказы в DV-стиле
  7. Как это живёт в пайплайне загрузки
  8. Плюсы и минусы Data Vault
  9. Когда DV стоит использовать, а когда нет
  10. Шпаргалка для собеседования

1. Зачем вообще нужен Data Vault?

Большинство знакомятся с хранилищами через две модели:

  • 3NF (Инмон) — нормализованное ядро: много таблиц, строгие связи, минимум дублирования.
  • Звезда (Star Schema) (Кимбалл) — витрины под отчёты: факт + несколько «плоских» измерений.

Этого хватает для:

  • 2–5 источников,
  • относительно стабильных схем,
  • задач типа «сделать отчёт для маркетинга/финансов».

Проблемы начинаются, когда:

  • источников становится десяток и больше (CRM, биллинг, ERP, сайт, мобильное приложение, партнёры, скоринги…);
  • схемы постоянно меняются: добавляются поля, сущности, новые связи;
  • появляются требования по аудиту и трассировке (то есть умению по шагам показать, откуда взялись данные и как они менялись): «покажите, откуда взялся вот этот показатель, по шагам».

Вот тут обычная 3NF/Звезда начинает скрипеть:

  • любое изменение источника → больно по ядру и витринам;
  • история размазана по разным местам (где-то SCD, где-то лог-таблицы, где-то вообще нет истории);
  • добавление нового источника превращается в мини-проект на месяц.

Data Vault 2.0 отвечает именно на эту боль:

Как сделать так, чтобы новый источник → это не «ремонт всего дома», а просто «докрутить ещё один модуль»?


2. Интуиция: DV как конструктор Lego

Классическая метафора DV — это конструктор из трёх типов деталей:

  • 🔴 Hub (Хаб)«кто/что это» Сущности: клиент, заказ, договор, счёт. Внутри: бизнес‑ключ (например, customer_id или contract_number) и техполя.

  • Link (Линк)«как они связаны» «Клиент сделал заказ», «договор относится к счёту». Внутри: ссылки на хабы и техполя.

  • 🟡 Satellite (Сателлит)«какие у них свойства и как они менялись» Атрибуты сущности (имя, email, статус, тариф) плюс история изменений.

Главная идея:

Идентичность, связи и атрибуты живут отдельно. Тогда изменения в одном не ломают другое.

На картинке это можно показать так:

erDiagram
    HUB_CUSTOMER ||--o{ SAT_CUSTOMER_INFO : имеет_атрибуты
    HUB_CUSTOMER ||--o{ LINK_ORDER_CUSTOMER : участвует_в
    HUB_ORDER ||--o{ LINK_ORDER_CUSTOMER : создан
    LINK_ORDER_CUSTOMER ||--o{ SAT_ORDER_STATUS : имеет_историю_статусов

    HUB_CUSTOMER {
        varchar customer_bk PK
        varchar record_source
        timestamp load_dttm
    }

    HUB_ORDER {
        varchar order_id PK
        varchar record_source
        timestamp load_dttm
    }

    LINK_ORDER_CUSTOMER {
        varchar link_key PK
        varchar order_id FK
        varchar customer_bk FK
        varchar record_source
        timestamp load_dttm
    }

    SAT_CUSTOMER_INFO {
        varchar customer_bk FK
        varchar hashdiff
        varchar name
        varchar email
        varchar city
        varchar record_source
        timestamp load_dttm
    }

        SAT_ORDER_STATUS {
            varchar link_key FK
            varchar hashdiff
            varchar status
            varchar record_source
            timestamp load_dttm
        }

Как это читать:

  • 🔴 HUB_CUSTOMER / HUB_ORDER — «этот клиент существует», «этот заказ существует»;
  • ⚪ LINK_ORDER_CUSTOMER — «именно этот заказ сделал именно этот клиент»;
  • 🟡 SAT_… — как менялись атрибуты (email, статус и т.п.) во времени.

На схеме выше для наглядности показаны бизнес-ключи (customer_bk, order_id, link_key), а в DDL-примерах дальше используются уже хэш-ключи (hk_*) — это два уровня детализации одной и той же модели.

3. Три типа таблиц в Data Vault 2.0

Чуть менее «сказочно», чуть более технично.

3.1. Hub — сущность и её бизнес-ключ

Hub содержит:

  • бизнес‑ключ (customer_bk, order_id, contract_number);
  • техническую информацию:
  • record_source — из какой системы пришла первая запись;
  • load_dttm — когда запись попала в DV;
  • иногда — хэш бизнес‑ключа (hk_customer).

Главные правила: * один бизнес‑ключ — один хаб (одна строка на сущность, без истории); * хаб не знает про атрибуты (имя, email) — только идентичность.

Простейший DDL‑скелет:

CREATE TABLE hub_customer (
    hk_customer     BYTEA        PRIMARY KEY,  -- хэш от BK
    customer_bk     VARCHAR(50)  NOT NULL,     -- business key
    record_source   VARCHAR(50)  NOT NULL,
    load_dttm       TIMESTAMP    NOT NULL
);

Конкретные типы данных (BYTEA, длины VARCHAR, детали hashdiff) и реализации хэш‑ключей можно не запоминать: на старте важнее понять саму идею — у сущностей есть стабильные ключи, а все изменения атрибутов мы записываем отдельными версиями в сателлитах.

Link описывает факт связи, например:

  • заказ ↔ клиент;
  • договор ↔ счёт;
  • карта ↔ клиент.

Примеры бизнес‑смыслов:

  • link_order_customer — «этот заказ принадлежит этому клиенту»;
  • link_contract_account — «этот договор привязан к этому счёту».

DDL‑эскиз:

CREATE TABLE link_order_customer (
    hk_order_customer  BYTEA PRIMARY KEY,
    hk_order           BYTEA NOT NULL,
    hk_customer        BYTEA NOT NULL,
    record_source      VARCHAR(50) NOT NULL,
    load_dttm          TIMESTAMP   NOT NULL
);

3.3. Satellite — атрибуты и история

Satellite хранит:

  • атрибуты хаба или линка;
  • историю изменений этих атрибутов.

Примеры:

  • sat_customer_info — имя, email, город клиента;
  • sat_customer_segment — сегмент, категория, риск‑профиль;
  • sat_order_status — статус заказа.

Типичные поля:

  • ссылка на HUB или LINK (hk_customer, hk_order_customer);
  • атрибуты (email, city, status и т.п.);
  • hashdiff — хэш от всех атрибутов, чтобы понять, изменилась ли строка;
  • record_source, load_dttm — источник и момент загрузки версии.
CREATE TABLE sat_customer_info (
    hk_customer    BYTEA      NOT NULL,
    hashdiff       BYTEA      NOT NULL,
    name           VARCHAR(100),
    email          VARCHAR(100),
    city           VARCHAR(50),
    record_source  VARCHAR(50) NOT NULL,
    load_dttm      TIMESTAMP   NOT NULL
);

Главная мысль: DV заставляет явно разделять идентичность, связи и атрибуты с историей. Это делает модель сложнее на вид, но гораздо устойчивее к изменениям источников.

Когда нужны именно бизнес-периоды действия («с/по»), их удобнее моделировать не в базовом сателлите, а отдельными effectivity-сателлитами или через PIT-таблицы в Business Vault.

4. Типы сателлитов в DV 2.0

Если вы только знакомитесь с DV, этот раздел можно прочитать по диагонали: для собеседования важно скорее знать, что такие роли бывают, чем разбираться во всех нюансах.

В DV 2.0 появилось разделение по «ролям» сателлитов. Главное, что стоит знать:

  • Descriptive Satellites — обычные атрибуты (имя, адрес, тариф) с историей.
  • Effectivity Satellites — фокус на периодах действия (valid_from / valid_to), очень похоже на SCD2.
  • Multi-Active Satellites — когда у сущности несколько одновременных значений (например, три активных телефона клиента).
  • Transactional Satellites — события, привязанные к одному хабу/линку (например, журнал изменений статуса).

На практике это разные DDL-«шаблоны» поверх одной и той же идеи: атрибуты + время → отдельная табличка.


5. Raw Vault и Business Vault

Обычно под «Data Vault» люди смешивают два слоя:

flowchart LR
    SRC[Источники] --> STG[STG / ODS]
    STG --> RAW[Raw Vault
Hubs, Links, Sats]
    RAW --> BV[Business Vault
PIT, Bridge, Derived]
    BV --> DM[Data Marts
Star Schema]
    DM --> BI[BI / ML]
  • Raw Vault — это про приём и хранение данных «как есть», но уже в форме Hub / Link / Satellite.
  • Business Vault — это про приведение этих данных в более «деловой» вид: с бизнес-правилами, PIT/Bridge и подготовленными представлениями.

5.1. Raw Vault — «всё прилетевшее, аккуратно разложенное по ящичкам»

Raw DV — первый слой поверх STG / ODS:

  • выравниваем ключи;
  • разбираем сущности по Hub / Link / Sat;
  • сохраняем всю историю изменений, не решая ещё, что такое «активный клиент» или «успешный заказ».

Характерные черты Raw Vault:

  • минимум бизнес-логики:

  • никаких правил вроде «клиент активен, если была хотя бы одна покупка за 90 дней»;

  • все источники показываются «как есть», только приведены к общим ключам;
  • структура стабильна: добавился новый источник → появился новый Satellite к тому же Hub.

5.2. Business Vault — «там, где из Lego собирают модули»

Business Vault (BV) — следующий слой над Raw DV:

  • здесь применяются бизнес-правила (что считать активным клиентом, как трактовать статусы);
  • здесь строятся вспомогательные структуры:

  • PIT-таблицы,

  • Bridge-таблицы,
  • агрегаты и derived-таблицы.

Именно из BV чаще всего строятся витрины в формате Звезды, к которым подключаются BI и отчётность.

Если сильно упростить:

  • Raw DV → «мы всё собрали»;
  • Business Vault → «мы это привели в вид, с которым удобно жить»;
  • DM → «мы вынесли это на витрину в понятной форме».

5.2.1. PIT-таблицы (Point-in-Time)

Раздел для любопытных: PIT-таблицы — уже продвинутая тема, на первом знакомстве с DV её можно смело пропустить и вернуться позже.

PIT (Point-in-Time) решает очень конкретную боль:

«Покажи, как объект выглядел на дату X, но так, чтобы запрос был простым».

Если у нас есть несколько сателлитов с историей (например, sat_customer_info, sat_customer_segment, sat_customer_risk), то без PIT любой запрос превращается в пачку условий as_of_date >= valid_from AND (valid_to IS NULL OR as_of_date < valid_to) (в effectivity-сателлитах или любых таблицах с периодами действия) или оконных функций.

Идея PIT:

Мы заранее считаем «словарь» вида:

для каждой пары (объект, дата) — какие версии сателлитов на эту дату актуальны.

Упрощённый пример структуры:

CREATE TABLE pit_customer_daily (
    hk_customer     BYTEA,
    as_of_date      DATE,
    hk_sat_info     BYTEA,  -- sat_customer_info на эту дату
    hk_sat_segment  BYTEA,  -- sat_customer_segment на эту дату
    load_dttm       TIMESTAMP
);

Внутри логически это выглядит так:

hk_customer as_of_date hk_sat_info hk_sat_segment
101 2023-06-10 hash_info_v1 hash_seg_v1
101 2023-06-11 hash_info_v2 hash_seg_v1
101 2023-06-12 hash_info_v2 hash_seg_v2

Дальше витрина продаж вместо сложных BETWEEN делает простой JOIN:

SELECT
    s.sale_date,
    s.amount,
    seg.segment,
    info.city
FROM fact_sales s
JOIN pit_customer_daily pit
  ON pit.hk_customer = s.hk_customer
 AND pit.as_of_date  = s.sale_date
JOIN sat_customer_segment seg
  ON seg.hk_customer = pit.hk_customer
 AND seg.hashdiff    = pit.hk_sat_segment
JOIN sat_customer_info info
  ON info.hk_customer = pit.hk_customer
 AND info.hashdiff    = pit.hk_sat_info;

Мы один раз дорого посчитали PIT (по расписанию), и дальше все витрины просто используют эту «справочную таблицу».

Коротко: PIT — это таблица, где заранее записано, какая версия данных была актуальна на дату X.

5.2.2. Bridge-таблицы

Тоже продвинутый приём: Bridge-таблицы чаще нужны в боевых хранилищах, чем на первых собеседованиях.

Bridge-таблицы отвечают на другой вопрос:

«Какие объекты в итоге связаны между собой через длинную цепочку Links?»

В Data Vault связь между двумя сущностями редко бывает «одним JOIN’ом». Чаще это цепочка:

  • клиент → договор → счёт → карта → транзакция;
  • подразделение → филиал → торговая точка → чек;
  • компания → дочерняя компания → проект → контракт → платёж.

Если каждый раз в витринах писать все эти JOIN’ы, запросы становятся:

  • длинными и хрупкими;
  • плохо читаемыми;
  • дублируются во множестве отчётов.

Идея Bridge:

Сделать отдельную таблицу, где заранее развернуть «финальные» пары:

CREATE TABLE bridge_customer_trx (
    hk_customer  BYTEA,
    hk_trx       BYTEA
    -- опционально: даты действия связи, тип связи и т.п.
);

Такую таблицу мы считаем в ETL, один раз по расписанию, а не в каждом запросе. Условный псевдокод построения:

INSERT INTO bridge_customer_trx (hk_customer, hk_trx)
SELECT DISTINCT
    c.hk_customer,
    t.hk_trx
FROM hub_customer c
JOIN link_customer_contract lcc    ON lcc.hk_customer = c.hk_customer
JOIN hub_contract ct               ON ct.hk_contract = lcc.hk_contract
JOIN link_contract_account lca     ON lca.hk_contract = ct.hk_contract
JOIN hub_account acc               ON acc.hk_account = lca.hk_account
JOIN link_account_card lac         ON lac.hk_account = acc.hk_account
JOIN hub_card card                 ON card.hk_card = lac.hk_card
JOIN link_card_trx lct             ON lct.hk_card = card.hk_card
JOIN hub_trx t                     ON t.hk_trx = lct.hk_trx;

После этого витрина может использовать уже готовый Bridge:

SELECT
    c.customer_bk,
    SUM(t.amount) AS total_amount
FROM hub_customer c
JOIN bridge_customer_trx bct
  ON bct.hk_customer = c.hk_customer
JOIN hub_trx t
  ON t.hk_trx = bct.hk_trx
WHERE t.trx_date >= CURRENT_DATE - INTERVAL '30 day'
GROUP BY c.customer_bk;

Сложный путь по Links мы спрятали внутрь bridge‑таблицы, а витрины работают с простой связкой «клиент ↔ транзакция».

Коротко:

  • PIT — про «какая версия атрибутов была на дату X»;
  • Bridge — про «какие объекты в итоге связаны друг с другом по длинному маршруту».

Bridge — это не обязательный элемент DV, а инструмент оптимизации. Он нужен тогда, когда цепочки Links становятся длинными и повторяются во многих отчётах.

5.2.3. Business-правила и derived-таблицы

Этот раздел полезен, чтобы увидеть, как DV помогает «прятать» повторяющуюся бизнес-логику, но для базового понимания Data Vault его можно оставить «на потом».

В BV логично размещать бизнес-логику, которая:

  • повторяется во многих отчётах;
  • достаточно стабильна.

Примеры:

  • флаг «активный клиент», который вычисляется на основе истории покупок и логинов;
  • «основной тариф», выбранный по набору правил из нескольких источников;
  • «чистый статус заказа», свёрнутый из цепочки статусов (created → paid → shipped → delivered / cancelled).

Это могут быть как отдельные Satellites / Links, так и логические таблицы BV с уже посчитанными флагами и агрегатами.

5.3. Граница между Business Vault и витринами (DM)

Важно проговорить границу:

  • Business Vault — ещё про данные и историю;
  • DM (Data Marts) — уже про конкретные бизнес-сценарии и удобство BI.

Витрины можно переделывать, не трогая BV, пока вы не меняете фундаментальные бизнес-правила.

5.4. Как выглядит связка Raw DV → BV → DM на примере

Мини-пример интернет-магазина:

  1. Raw Vault:

  2. hub_customer, hub_order;

  3. link_order_customer;
  4. sat_customer_info, sat_order_status, sat_customer_segment.

  5. Business Vault:

  6. pit_customer_daily — срез клиента по дням;

  7. bv_customer_flags — активность, VIP-статусы, сегменты;
  8. bridge_customer_order — связи клиент ↔ заказ с удобными ключами.

  9. DM / Star Schema:

  10. dm.fact_sales — факт продаж;

  11. dm.dim_customer — уже плоское измерение с нужными полями (email_current, segment, is_active_90d и т.п.);
  12. dm.dim_date, dm.dim_product и другие измерения.

В результате:

  • Raw DV — технически корректный, историчный и некрасивый слой;
  • BV — рабочий слой для инженеров и продвинутых аналитиков;
  • DM — привычная Звезда для всех остальных.

6. Пример: клиент и заказы в DV-стиле

Возьмём мини‑пример всё того же интернет‑магазина.

У нас есть:

  • клиент с customer_id = 101, у которого иногда меняется email и город;
  • два заказа: order_id = 5001 и order_id = 5002;
  • статусы заказов: created → paid → shipped → delivered.

Что появляется в Raw Vault

В Data Vault это раскладывается на несколько таблиц:

  • hub_customer — по одной строке на каждого клиента (BK = customer_id).
  • hub_order — по одной строке на каждый заказ (BK = order_id).
  • link_order_customer — связь «какой заказ сделал какой клиент».
  • sat_customer_info — история атрибутов клиента (имя, email, город).
  • sat_order_status — история статусов заказа.

Примерно так это выглядит логически:

HUB_CUSTOMER
--------------------------------------
customer_bk | record_source | load_dttm
--------------------------------------
101         | CRM           | 2023-01-10 10:00

SAT_CUSTOMER_INFO
customer_bk | load_dttm           | hashdiff | email              | city
---------------------------------------------------------------------------
101         | 2023-01-10 10:00    | ...      | a@example.com      | Moscow
101         | 2023-06-01 09:00    | ...      | alice@newmail.com  | Moscow

HUB_ORDER
--------------------------------------
order_id    | record_source | load_dttm
--------------------------------------
5001        | SHOP          | 2023-06-10 12:00
5002        | SHOP          | 2023-06-11 09:30

SAT_ORDER_STATUS
order_id | load_dttm           | hashdiff | status
--------------------------------------------------------------
5001     | 2023-06-10 12:00    | ...      | created
5001     | 2023-06-10 12:05    | ...      | paid
5001     | 2023-06-11 09:00    | ...      | shipped
...      | ...                 | ...

Ключевая идея: любое изменение (email, статус) — это новая строка в соответствующем Satellite.

Как из этого получить витрину продаж

Дальше нам нужно привычное измерение dim_customer и факт fact_sales в формате Звезды.

Обычно цепочка выглядит так:

  1. В Business Vault строим PIT‑таблицу по клиентам:

  2. для каждой даты (или дня, или часа) знаем, какая строка из sat_customer_info была актуальна.

  3. Строим витрину dm.dim_customer:

  4. на выбранную дату берём нужную версию из sat_customer_info;

  5. добавляем флаги/сегменты из других Satellites/BV‑таблиц.
  6. Строим факт dm.fact_sales, где каждая строка — заказ или позиция заказа.

Итог: на витрине мы видим «плоского» клиента (одна строка → текущее имя/город/email на момент заказа), хотя внутри DV лежит полная, аккуратно разложенная история.

7. Как это живёт в пайплайне загрузки

Посмотрим теперь, как DV вписывается в обычный ETL/ELT‑пайплайн.

Типичный поток выглядит так:

  1. STG / ODS — приём и первичная обработка

  2. Подключаемся к источникам (CRM, биллинг, сайт, партнёрские выгрузки).

  3. Забираем инкременты (CDC, выгрузки по расписанию, API).
  4. Приводим типы данных, чистим очевидный мусор, нормализуем форматы дат и т.п.

  5. Raw Vault — приземление в Hub / Link / Satellite

  6. Из STG/ODS считаем хэш‑ключи для бизнес‑ключей (HK для Hubs).

  7. Создаём/обновляем Hubs — если бизнес‑ключ новый, заводим запись.
  8. Создаём/обновляем Links — фиксируем связи между сущностями.
  9. Для Satellites считаем hashdiff по атрибутам и добавляем новые строки только если что‑то реально изменилось.

  10. Business Vault — бизнес‑правила и ускорители

  11. Строим PIT‑таблицы, чтобы быстро получать срез «на дату X».

  12. Строим Bridge‑таблицы для сложных цепочек связей.
  13. Вычисляем стабильные бизнес‑флаги и derived‑атрибуты (активность, сегменты, статусы).

  14. DM / Star Schema — витрины для отчётов и аналитики

  15. На основе BV собираем факты и измерения в формате Звезды.

  16. Под это уже настраиваются BI‑инструменты, отчётность, дашборды.

Чем это отличается от классического «STG → ODS → DDS → DM»:

  • слой DDS в DV‑подходе часто фактически превращается в Raw+Business Vault;
  • вместо одной «большой» нормализованной схемы ядра у нас набор Lego‑модулей (Hubs/Links/Sats), которые за счёт хэш‑ключей и независимой загрузки доменов проще масштабировать и развивать/грузить параллельно.

DV не отменяет STG/ODS/DM — он скорее раскладывает слой DDS на более мелкие и управляемые детали.

8. Плюсы и минусы Data Vault

8.1. Плюсы

  • 📜 История «из коробки» Каждое изменение — отдельная запись в сателлите. Ничего не перезатирается.

  • 🧩 Лёгкое добавление источников Новый источник с теми же сущностями = новые сателлиты к тем же хабам.

  • 🔎 Трассировка и аудит Видно, из какого источника, когда и с какими атрибутами прилетела каждая строка.

  • 👥 Параллельная работа команд Разные домены в DV практически не блокируют друг друга.

8.2. Минусы

  • 🧠 Высокий порог входа Нужно понимать SCD, хэш‑ключи, нагрузку на JOIN, паттерны загрузки.

  • 📈 Больше таблиц и JOIN’ов Даже простой запрос превращается в «HUB + LINK + 2–3 SAT + PIT».

  • 🛠 Нужна дисциплина Забыли заполнить record_source/load_dttm — потеряли часть аудита.

  • Плохо подходит для MVP Для 2–3 источников DV обычно дороже, чем классическая 3NF/Звезда.

9. Когда DV стоит использовать, а когда нет

Подходит, если:

  • у вас зоопарк источников (5+ систем, которые ещё и меняются);
  • важна полная история и аудит (финтех, гос, телеком, крупный банк);
  • команда нацелена на долгую жизнь DWH, а не одноразовый отчёт;
  • есть люди, готовые жить в этой модели (архитектор, data engineer’ы).

Лучше не начинать с DV, если:

  • это первый DWH в компании;
  • 1–3 источника и нет жёстких требований по аудиту;
  • команда малая (1–2 инженера + аналитик) и сроки жмут;
  • задача звучит как «дайте отчёты к кварталу», а не «построим платформу на 5 лет».

В таких случаях честнее (и дешевле) начать с:

stg → ods → dds (3NF/простая Звезда с SCD2) → dm (Звезда)

А DV оставить как следующий шаг, когда появятся реальные боли, которые он решает.

10. Шпаргалка для собеседования

Если нужно быстро объяснить, что такое Data Vault:

  • Data Vault — это модель хранилища, которая разделяет идентичность (Hubs), связи (Links) и атрибуты с историей (Satellites), чтобы проще переживать изменения источников.
  • В DV есть три типа таблиц: Hub (бизнес-ключи сущностей), Link (связи между сущностями) и Satellite (атрибуты и их история).
  • Raw Vault — слой, где данные складываются «как есть» в виде Hub/Link/Sat, Business Vault — слой с бизнес-правилами, PIT/Bridge и подготовленными представлениями для витрин.
  • История в DV хранится «из коробки»: каждое изменение атрибутов — новая строка в сателлите, прошлые значения не затираются.
  • DV хорошо подходит, когда много источников, они часто меняются и важен аудит; для маленького, простого DWH обычно хватает 3NF/Звезды.
  • Для собеседования важно уметь связать всё вместе: объяснить Hub/Link/Satellite, отличия Raw и Business Vault и то, что хэш-ключи и независимые сателлиты позволяют параллельно загружать разные сущности, не ломая историю и аудит.