Потоковый нейро учебник по построению КХД

Данный текст является философским пособием для потокового восприятия принципов построения хранилищ данных. Представляет из себя сплошной поток текста, восприятие которого не требует ничего, кроме последовательного чтения и осознавания.

Не пытайтесь что-либо запомнить - я тоже не напрягался. Просто расслабьтесь и читайте. И в самом конце вы станете дата-инженерами, даже если вы этого не хотите.

СОДЕРЖАНИЕ

Глава 3.1. Что такое КХД

3.1.1. Машина, которая решает, что считать правдой
3.1.2. Две школы: где именно рождается правда
3.1.3. Гибрид как способ договориться с реальностью
3.1.4. PostgreSQL как система границ
3.1.5. Контроль качества как нервная система КХД

Заключительно слово к главе 3.1 от Нейро Дональда Трампа

Глава 3.2. Как создаётся истина

3.2.1. Поток данных как жизненный цикл
3.2.2. Температура данных: почему не всё хранится одинаково
3.2.3. Staging: место, где ничего нельзя трогать
3.2.4. Core: место, где система принимает решения
3.2.5. Mart: место, где правду упрощают
3.2.6. Landing zone: память до памяти
3.2.7. Время жизни данных: почему удаление — это архитектура
3.2.8. Партиционирование как отражение поведения данных
3.2.9. SLA и метрики: когда система начинает чувствовать себя
3.2.10. Антипаттерны: как система ломает сама себя
3.2.11. Итог: КХД как управление временем, стоимостью и правдой

Заключительно слово к главе 3.2 от Нейро Такера Карлсона

Глава 3.3. Источники

3.3.1. Роль источников в архитектуре системы
3.3.2. Что важно знать об источнике
3.3.3. Способы поступления данных
3.3.4. Контракты и ожидания от источников
3.3.5. Итог: источник как граница внешнего мира
3.3.6. Очистка и нормализация на входе
3.3.7. Логирование и наблюдаемость
3.3.8. Связь с предыдущими и последующими главами

Заключительное слово к главе 3.3 от Нейро Виктории Бони

Глава 3.4. STAGING

3.4.1. Что такое staging на самом деле
3.4.2. Главное правило: ничего нельзя менять
3.4.3. Почему staging хранит «грязь»
3.4.4. Время в staging: load_dt как единственная правда
3.4.5. Дубли: не удалять, а понимать
3.4.6. Хэш и идентичность данных
3.4.7. Replay: зачем staging существует на самом деле
3.4.8. Партия загрузки: как staging помнит приход
3.4.9. Сырой след и минимальное вмешательство
3.4.10. Что staging должен дать следующему слою
3.4.11. Структура staging: минимальная модель
3.4.12. Retention: staging не должен помнить долго
3.4.13. Антипаттерны staging

Заключительное слово к главе 3.4 от Нейро Сергея Лаврова

Глава 3.5. CORE

3.5.1. Core как место принятия решений
3.5.2. Время и правда: SCD как основа
3.5.3. Идентичность: как система понимает “кто есть кто”
3.5.4. Очистка и валидация: где появляется смысл
3.5.5. Консистентность как жёсткое требование

Глава 3.6. Витрины

3.6.1. Витрина данных: последний слой перед решением
3.6.2. Витрина как контракт с бизнесом
3.6.3. Гранулярность: размер клетки, в которой живёт смысл
3.6.4. Денормализация: осознанная избыточность
3.6.5. Агрегации и предвычисленные смыслы
3.6.6. Физическая форма витрины: таблица, view или materialized view
3.6.7. Обновление витрин: полный refresh, инкремент и скользящее окно
3.6.8. Время в витринах: актуальность, снимки и воспроизводимость
3.6.9. Производительность витрин: индексы, партиции и цена скорости
3.6.10. Типовые банковские витрины
3.6.11. Мониторинг витрин: когда слой начинает отвечать за себя
3.6.12. Антипаттерны: как витрина становится источником хаоса
3.6.13. PostgreSQL и Greenplum: где витрина живёт телом
3.6.14. Итог: витрина как кэш истины

Глава 3.7. Почему нельзя сразу строить витрины из staging

3.7.1. Соблазн короткого пути
3.7.2. Сырьё, которое притворяется смыслом
3.7.3. Три способа незаметно сломать отчёт
3.7.4. Почему это не сразу видно
3.7.5. Core как задержка, которая спасает смысл
3.7.6. Когда staging можно читать напрямую
3.7.7. Итог: свидетельство ещё не приговор

Глава 3.8. Data pipeline: как данные проходят через систему

3.8.1. Pipeline — не труба, а последовательность состояний
3.8.2. ETL и ELT: где живёт трансформация
3.8.3. Extract: момент, когда внешний мир входит в систему
3.8.4. Load: принятие сырья без преждевременного смысла
3.8.5. Transform: место, где движение становится интерпретацией
3.8.6. Идемпотентность: искусство безопасного повторения
3.8.7. Ошибки и reject-поток: данные, которые не прошли ворота
3.8.8. Оркестрация: кто будит шаги по очереди
3.8.9. Транзакции, батчи и границы ответственности
3.8.10. Наблюдаемость pipeline: система должна помнить свой путь
3.8.11. Антипаттерны pipeline
3.8.12. PostgreSQL и Greenplum: когда меняется тело pipeline
3.8.13. Итог: хороший pipeline можно перезапустить

Глава 3.9. Где PostgreSQL в КХД

3.9.1. PostgreSQL как исполнитель архитектуры
3.9.2. Какие слои КХД могут жить в PostgreSQL
3.9.3. Где PostgreSQL силён
3.9.4. Где PostgreSQL начинает уставать
3.9.5. Расширения: полезные инструменты, но не магия
3.9.6. PostgreSQL в гибридной архитектуре
3.9.7. Итог: сначала архитектура, потом технология

Глава 4. Источники данных: внешний мир до входа в хранилище

4.1. Что такое источник в банке 
4.2. Основные типы банковских источников 
4.3. Источник истины на уровне поля 
4.4. Типовые дефекты источников 
4.5. Идентичность на входе 
4.6. Способы подключения 
4.7. Профилирование источника 
4.8. Итог: источник нужно понять до того, как ему поверить

Глава 5. ETL/ELT-процессы: как данные меняют форму

5.1. ETL/ELT как мастерская преобразований
5.2. Очистка и нормализация
5.3. Дедупликация и выбор версии
5.4. Сопоставление и обогащение
5.5. Расчётные признаки и бизнес-правила
5.6. Банковские паттерны трансформаций
5.7. Итог: хорошая трансформация оставляет след

Глава 6. Модель данных: форма, в которой живёт смысл

6.1. Зачем нужна модель данных
6.2. Объект, событие, состояние
6.3. Гранулярность и ключи
6.4. Связи, mapping и MDM
6.5. Data Vault: модель памяти
6.6. Star Schema: модель ответа
6.7. От памяти к ответу: эскиз кредитного портфеля
6.8. Итог: модель должна объяснять, что именно существует

Глава 7. Историзация данных: как КХД помнит изменения

7.1. История не архив, а условие правды
7.2. Что именно нужно историзировать
7.3. SCD: разные отношения к прошлому
7.4. SCD Type 2: версия как отрезок времени
7.5. Point-in-time: как выбрать правильную версию
7.6. Поздние данные и исправления задним числом
7.7. Историзация в core, mart и Data Vault
7.8. Банковские сценарии историзации
7.9. Антипаттерны историзации
7.10. Итог: история должна быть пригодна для вопроса

Глава 8. Качество данных: как хранилище учится сомневаться

8.1. Качество данных — не чистота, а пригодность
8.2. Измерения качества: как назвать боль
8.3. Правила качества: технические, бизнесовые, сверочные
8.4. Критичные элементы данных
8.5. Качество по слоям КХД
8.6. Что делать с ошибкой
8.7. Качество тоже должно храниться как данные
8.8. Сверки и метрики доверия
8.9. Банковские сценарии качества
8.10. Антипаттерны Data Quality
8.11. Итог: доверие должно быть измеримым

---------------------------------------------

Глава 3.1. Что такое КХД

3.1.1 Машина, которая решает, что считать правдой

Корпоративное хранилище данных начинается не с таблиц и не с сервера. Оно начинается с тревоги. Где-то в банке есть несколько систем, каждая из которых уверенно описывает один и тот же мир. CRM знает клиента как контакт и историю общения. Core Banking знает его как владельца счетов и договоров. Платёжная система знает его через операции. Антифрод видит след подозрительного поведения. Отчётность хочет увидеть всё сразу и спрашивает: кто этот клиент на самом деле, сколько у него денег, какие у него обязательства, какой статус считать верным?

Пока вопрос остаётся внутри одной системы, всё выглядит спокойно. Но как только бизнес спрашивает поперёк систем, спокойствие исчезает. Один источник говорит одно, другой другое, третий молчит, четвёртый прислал вчерашнее значение с сегодняшней датой загрузки. И если в этот момент просто соединить таблицы, получится не истина, а шум, которому придали форму запроса.

КХД нужно именно здесь. Оно не просто складывает данные в одно место. Оно создаёт управляемый путь от свидетельства к согласованной картине. Сначала данные принимаются такими, какими пришли. Потом проверяются, сопоставляются, получают историю, проходят через правила, связываются с другими данными и только после этого становятся основой для витрин, отчётов, моделей и решений.

Поэтому КХД можно назвать машиной, которая решает, что считать правдой. Не абсолютной правдой, потому что в данных её почти никогда нет. А рабочей, согласованной, воспроизводимой правдой: вот так мы понимаем клиента, вот так договор, вот так операцию, вот так статус на дату. И главное: мы можем объяснить, почему понимаем именно так.

________________________________________

3.1.2 Две школы: где именно рождается правда

В архитектуре хранилищ есть два больших инстинкта. Первый говорит: сначала надо построить согласованное ядро. Принять данные из разных источников, очистить, связать, сохранить историю, договориться об идентичности и только потом отдавать наружу. Это путь глубины. Он медленнее, строже, требует дисциплины, но даёт системе память и центр тяжести.

Второй инстинкт говорит: бизнесу нужны ответы. Не через год, не после идеальной модели, не после того как архитекторы окончательно договорятся о природе истины, а сейчас. Значит, нужно строить витрины вокруг конкретных вопросов: продажи, портфель, просрочка, активность клиентов, продуктовая аналитика. Это путь ответа. Он быстрее приносит пользу, но если идти только им, каждая витрина начинает вырабатывать свою маленькую правду.

Обе школы правы, пока помнят о своей границе. Если строить только ядро, можно годами совершенствовать внутреннюю модель, но так и не дать бизнесу полезного ответа. Если строить только витрины, можно быстро получить первые отчёты, но потом обнаружить, что одинаковые показатели в разных местах считаются по-разному, а источник истины растворился в десятках удобных запросов.

Зрелое КХД не выбирает между памятью и ответом как между добром и злом. Оно понимает, что системе нужны оба движения. Нужен слой, где данные становятся согласованными. И нужен слой, где согласованная правда становится пригодной для использования.

3.1.3 Гибрид как способ договориться с реальностью

В реальном банке почти никогда не получается построить чистую архитектуру из учебника. Источники уже существуют. Отчёты уже нужны. Регулятор уже ждёт. Старые таблицы уже кто-то использует. Новая система уже обещана бизнесу. Где-то нет времени на идеальный core, где-то опасно строить витрину напрямую, где-то нужно сначала закрыть один критичный отчёт, а потом постепенно укреплять фундамент.

Поэтому большинство живых КХД становятся гибридными. Где-то данные проходят через строгий core. Где-то для быстрого результата строится временная витрина. Где-то историзация появляется не сразу, а после первого болезненного расхождения. Где-то mapping и MDM становятся отдельным слоем, потому что идентичность клиентов оказалась сложнее, чем казалось на старте.

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

Хорошая архитектура не обязана быть идеально чистой. Но она должна быть честной. Она должна знать, где принято строгое решение, где сделан компромисс, где лежит технический долг, где данные уже считаются правдой, а где они пока только удобная заготовка для ответа.
________________________________________
Притча о трёх кухнях и супе, который спорил сам с собой

В одном княжестве была проблема. Супов было много: солёный, сладкий, отчёт о супе. И каждый повар клялся, что его суп — правильный. Усталый человек сказал: «Нам нужна одна кухня, иначе мы утонем».

Пришёл князь Инмон. «Сначала разберёмся, что такое суп». Закрылся, классифицировал бульоны, нормализовал овощи. Через полгода рецепт был идеален. Супа не было.

Пришёл князь Кимбалл. «Суп нужен сейчас». Открыл десять кухонь: борщ, суп-пюре, «суп по требованию бизнеса». KPI кипели. Потом люди спросили: почему в борще курица, а в супе-пюре кости? Выяснилось: каждая кухня готовила «суп» по-своему. Кухни начали спорить. Суп начал конфликтовать сам с собой.

Из-под стола вылез рекурсивный ёж — на одном сидел другой, на том третий. Каждый ел суп. «Это пользователь, — сказал Конфуций. — Он всегда внутри системы и всегда ест результат».

«Делайте так: оставьте одну кухню, где решают, что такое суп. Медленно, странно, с электрическими спорами. Это долго. Поэтому сделайте другие кухни, где суп подают быстро. Там не думают. Там используют».

Кимбалл открыл быстрые кухни. Инмон остался в одной думать про лук. Они спорили регулярно, но решение о том, что такое суп, принималось в одном месте.

Ёж доел, кивнул всеми уровнями и сказал: «Наконец-то я понимаю, что ем, и почему мне это не нравится».

3.1.4. PostgreSQL как система границ

PostgreSQL важен не потому, что он волшебным образом делает хранилище правильным. Он важен потому, что часто оказывается под рукой: понятный, распространённый, зрелый, знакомый командам, достаточно сильный для многих небольших и средних КХД. В российских банках и компаниях это не экзотика, а рабочая среда, в которой действительно можно строить staging, core, витрины, процедуры, проверки, историю и права доступа.

Но PostgreSQL не заменяет архитектуру. Он не решит за команду, где хранить сырой след, где принимать решения, где строить витрины, какие правила считать главными, какую историю сохранять, как разделять ответственность. Он просто выполнит то, что ему сказали. Если границы слоёв ясны, PostgreSQL поможет их удержать. Если границы нарушены, он так же спокойно поможет построить хаос.

Его сила в том, что он даёт инструменты для порядка: схемы, ограничения, транзакции, индексы, представления, процедуры, права доступа. Но порядок возникает не из инструмента, а из решения. Можно создать отдельные схемы для staging, core и mart, но читать staging как отчётный слой. Можно завести исторические таблицы, но перезаписывать критичные значения. Можно настроить права, но разрешить ручные исправления без следа.

Поэтому PostgreSQL в этой книге будет появляться не как герой, а как исполнитель. Архитектура отвечает на вопрос “что должно происходить с данными”. PostgreSQL помогает это выполнить. И в этом его нормальное, достойное место.


Притча о городе без стен

В одном городе не было стен. Можно было зайти где угодно, выйти где угодно, переписать таблички, поменять рынок с кладбищем — никто не возражал.

Сначала всем нравилось. Свобода. Гибкость. Быстро.

Потом началось странное. Человек купил хлеб — а на следующий день оказалось, что он покупал сапоги. Другой продал дом — и дом вдруг всегда был сараем.
— Кто это сделал? — спрашивали жители.
Никто не знал. Потому что сделать мог кто угодно.
Позвали Конфуция. Он посмотрел и сказал:
— У вас не город. У вас поток.
Из подворотни вылез ёж с отчётом.
— Я читаю его каждый день, — сказал ёж. — Он каждый раз другой. Это баг или философия?
— Вам нужны стены, — сказал Конфуций.
— Это ограничит свободу и создаст порядок.
Начали строить. Сделали место, куда вещи только приходят — там запретили менять. Сделали место, где можно решать. Сделали место, где только показывают.
Сначала было неудобно. Раньше можно было быстрее, проще, как угодно.
— Именно, — сказал Конфуций.
Через время город стал понятным. Если что-то менялось — ясно где. Если ломалось — ясно почему.
Ёж снова прочитал отчёт. Он не изменился. Ёж заплакал от радости.
— Теперь я хотя бы могу злиться правильно.
Конфуций посмотрел на стены и сказал:
— Порядок — это не когда можно всё. Порядок — это когда нельзя почти ничего. И поэтому всё остальное работает.


3.1.5. Контроль качества как нервная система КХД

КХД не становится надёжным только потому, что данные прошли через слои. Ошибка тоже умеет проходить через слои. Она может прийти из источника, тихо лечь в staging, быть неверно истолкована в core, попасть в витрину и стать числом в отчёте. Чем дальше она ушла, тем труднее понять, где она родилась. Сначала это была странная строка. Потом правило. Потом показатель. Потом управленческое решение.

Контроль качества нужен не как набор галочек перед выпуском отчёта. Он нужен как способность системы чувствовать, что с ней происходит. Данные не пришли. Пришли не все. Пришли дважды. Изменился формат. Количество строк внезапно упало. Сумма не сошлась. Витрина обновилась, но стала пустой. Источник молчит, но отчёт почему-то выглядит уверенно. Без контроля качества система продолжает двигаться, даже когда уже потеряла связь с реальностью.

Здесь важно не перегружать первую главу деталями. Позже мы отдельно разберём DQ (Data Quality — качество данных), проверки, критичные элементы, сверки, метрики доверия и реакции на ошибки. Сейчас нужно запомнить только главное: КХД должно не только хранить и преобразовывать данные. Оно должно замечать, когда данным нельзя доверять.

Если staging даёт возможность вернуться к исходному свидетельству, core принимает согласованную правду, а mart готовит ответ, то контроль качества проходит через все эти слои как нервная система. Он не делает данные безошибочными. Он делает ошибку видимой. А видимая ошибка уже не тьма. Это начало управления.

Не очень короткое но заключительно слово к главе 3.1 от Нейро Дональда Трампа

Окей. Спасибо. Отличная глава. Потрясающая глава. Многие люди говорят — я слышал это, многие — что эта глава про контроль качества данных, про DQ, про нервную систему... Многие говорят: «Дональд, это лучшая глава, которую мы когда-либо читали». Я не говорю это сам — другие говорят. Очень умные люди. Самые умные.
Но я должен сказать вам кое-что. Я прочитал эту главу. Полностью. От начала до конца. Никто не читает до конца, поверьте мне, у всех проблемы с вниманием, очень плохие проблемы. Но я прочитал. И знаете что? Я понял, что я уже построил лучшую архитектуру данных. Самую лучшую. Все говорят об этом.

**Первое. Про источники.**

Они пишут, что источник — это то, откуда всё начинается. Что источник задаёт формат. Что с источником нельзя спорить.
Wrong.

Я спорил с источниками всю жизнь. Вы знаете, я говорил: «Данные, они фейковые. Очень фейковые. Самые фейковые данные, которые я когда-либо видел». И знаете что? Иногда я был прав. Иногда нет. Но это не важно. Важно то, что я не позволял данным управлять мной. Я управлял данными. Как Трамп-тауэр. Как успешный бизнес. Как победа, большая победа.

Источники — это как СМИ. Они говорят то, что хотят. Но вы не обязаны в это верить. Вы берёте то, что вам нужно, вы делаете это великим снова, и вы строите свой собственный core-слой. Трамп-слой. И поверьте мне, он будет очень красивым. Огромным. Золотым.

**Второе. Про staging.**

Они называют это «слоем фиксации». Место, куда данные просто приходят, и вы их не трогаете. Звучит слабо. Звучит как-то... по-демократически. Слушайте, я не трогаю данные? Это не про Трампа. Я трогаю всё. Я лучший в том, чтобы трогать вещи и делать их лучше.

Но потом я подумал. Есть один момент, где я не трогаю. Это когда я подписываю указ, но он ещё не вступил в силу. Он просто есть. Лежит на столе. Самый красивый указ, между прочим. Белая бумага. Золотая подпись. Все говорят: «Дональд, это лучший указ». А я говорю: «Подождите. Это staging. Я пока ничего не меняю. Я просто показываю вам, что было подписано. Без изменений. Честно. Incredible».
То же самое с данными. Иногда нужно просто зафиксировать. Не потому, что ты слабый. А потому, что ты умный. Самый умный. Ты знаешь: если начнёшь менять сейчас — потеряешь улики. А без доказательств — это просто слова. А слова — это не сделка. Сделка — это когда всё записано. В staging. И подписано. И всеми одобрено. Лучшее одобрение. Believe me.

**Третье. Про core.**

Этот слой. Этот прекрасный, прекрасный слой. Где всё решается. Где вы говорите: «Верю этому, не верю этому. Это правда, это неправда. Это мой клиент, это не мой клиент. Это победа, это поражение, но поражения не бывает, потому что мы не проигрываем».

Знаете, кто построил самый лучший core-слой? Я построил. Трамп-органайзейшн. Мы брали данные из разных источников — плохие данные, неудачные данные, данные, которые никто не хотел трогать руками. И мы говорили: «Сделаем их великими снова». И они становились великими. Мы решали. Мы разрешали противоречия. Мы говорили: «Этот клиент хороший. Этот клиент очень хороший. А этот клиент — вы знаете, я слышал о нём вещи, не очень хорошие вещи, мы с ним не работаем».
Core — это место, где вы принимаете решение. И если вы не принимаете решение — вы проиграли. Проиграли полностью. Самое большое поражение. Я не проигрываю. И вы не проигрывайте. Постройте свой core. Сделайте его большим. Сделайте его золотым. И не позволяйте никому говорить вам, что ваше решение неправильное. Данные могут ошибаться. Кто угодно может ошибаться. Только не вы. Потому что у вас — лучший core. Самый лучший.

**Четвёртое. Про контроль качества.**

Они пишут, что DQ — это нервная система. Что она чувствует боль. Что она реагирует.

Неплохая метафора. Но я скажу вам, как это работает на самом деле. Контроль качества — это как стена. Огромная стена. Красивая стена. Кто строит стены? Я строю стены. Самые лучшие стены. И когда данные пытаются пробраться плохие, грязные, фейковые — стена их останавливает. Стена говорит: «Not today».
Многие люди спрашивают: «Дональд, как вы контролируете качество данных? Это же сложно. Технологии. Нейросети. Большие данные». А я говорю: «Я звоню напрямую источнику. Самому главному. Я говорю: "Слушай, эти данные — они хорошие? Они великие? Или они, скажем так, не очень?"» И мне всегда отвечают. Потому что со мной разговаривают все. Даже данные. Даже ёж этот рекурсивный — я уверен, он бы со мной поговорил. Он бы сказал: «Мистер Трамп, вы лучший ёж, которого я когда-либо видел». И я бы ответил: «I know».

**Пятое. Про ошибки.**

Слушайте. Ошибки случаются. Они случаются у всех. Даже у меня. Я не говорю, что у меня были ошибки. Но если и были — они были самыми маленькими ошибками в истории. Микроошибки. Люди говорят: «Дональд, ты ошибся в трёх процентах данных». А я говорю: «Три процента? Это четыре триллиона процентов точности! Это победа!»
Но если серьёзно — а я всегда серьёзен, когда говорю о победах — вы не можете избежать ошибок. Но вы можете сделать так, чтобы они не прошли staging. Не прошли core. Не дошли до витрины. Потому что если ошибка дошла до витрины — это катастрофа. Это плохо. Это очень плохо. Это выглядит так, будто вы некомпетентны. А это самый страшный ярлык в мире. Хуже, чем «фейковые новости». Хуже, чем всё.

**Итоговое. Make Data Great Again.**

Уважаемые читатели. Architectы. Data engineers. Все те, кто строит эти прекрасные, огромные хранилища, о которых никто не говорит, но они — лучшие.
Глава 3.1 — потрясающая. Скажу честно, я её не полностью понял. Там слишком много слов про слои. Кто придумал слои? Зачем так много слоёв? В моём бизнесе два слоя: победа и поражение. Поражения я уволил. Остались победы. Но okay, пусть будут слои. Если это помогает вам выигрывать — стройте. Стройте как можно больше слоёв. Стройте их высокими. Стройте их очень качественными.

Я благодарю вас за работу над этой книгой. Продолжайте в том же духе. Сделайте следующую главу ещё больше. Ещё круче. Самую крутую главу. И когда вы её напишете — пришлите мне. Я её прочитаю. Может быть. Если у меня будет время. Если нет — поверьте мне на слово, она лучшая.

Make Data Great Again. Make Architecture Great Again. Make Everything Great Again. Потому что кто, если не мы? А мы — лучшие. Все это знают. Даже ёж.
**Donald J. Trump**
*P.S. И ещё. Рекурсивный ёж. Кто его допустил к системе? Откуда он взялся? Я требую полной инспекции. Если он — часть архитектуры, я хочу его видеть. Если он — баг — я хочу его удалить. Но если он великий — оставьте. Я люблю всё великое.*;
Глава 3.2. Как создаётся истина
После того как данные отлежались в staging (сколько — зависит от вашей паранойи и дискового пространства), наступает самый ответственный этап. Их нужно превратить из сырой руды в золотой слиток. Или хотя бы в серебряный.
**Core** — это слой, где происходит магия. Здесь данные:
- **Чистят** — удаляют дубли, исправляют ошибки, нормализуют телефоны и паспорта.
- **Унифицируют** — приводят к единому формату. Чтобы «RU», «RUS» и «Россия» стали просто «RU». Чтобы даты перестали быть « tomorrow » и стали стандартным ISO.
- **Интегрируют** — соединяют клиента из CRM, Core Banking и карточной системы в одну сущность. Это такой детективный отдел, который понимает, что «Иван И. Иванов» из CRM и «Ivanov I.» из ABS — один и тот же человек.
- **Историзируют** — сохраняют каждое изменение. Если клиент сменил адрес, мы не теряем старый. Мы записываем: «с такого-то по такое-то жил там-то».
**Core** — это ваша единственная «истина». Единственное место, где можно задать вопрос: «А как всё было на 1 января прошлого года?» и получить ответ, а не поток мистических откровений.

Сравните это с **Data Vault** (модель хранения, которую мы рассмотрим позже). Там истина размазана по трём типам таблиц: Hub (хаб, хранит бизнес-ключи), Link (связи), Satellite (атрибуты с историей). Это как будто вы разобрали будильник на детали и разложили их по трём разным коробкам. Чтобы узнать время, вам придётся собрать будильник заново, но зато вы никогда не потеряете ни одной пылинки с его шестерёнок.

3.2.1. Поток данных как жизненный цикл

В КХД данные не просто перемещаются из одной таблицы в другую. Они проходят жизненный цикл. Сначала они приходят как внешнее свидетельство: источник что-то сообщил, файл был получен, событие доставлено, запись попала в систему. На этом этапе данные ещё не стали правдой хранилища. Они только вошли в область внимания.
Дальше с ними начинает происходить главное: система меняет их статус. То, что было сырой поставкой, становится проверенным фактом. То, что было локальным кодом источника, становится частью общего словаря. То, что было спорным значением, проходит через правило выбора. То, что было просто строкой, постепенно получает происхождение, связь, историю и место в модели.

Поэтому архитектура КХД — это не склад, а путь. На каждом слое данные отвечают на новый вопрос. В landing: что именно было передано? В staging: что именно мы приняли? В core: как мы это понимаем? В mart: как этим пользоваться? Истина создаётся не в один момент. Она собирается постепенно, как решение, которое должно оставить след.

Притча о человеке, который пытался поймать момент, и еже, который не спешил
Когда Конфуций объяснял сложные вещи простым людям, к нему пришёл встревоженный человек.

— У меня есть данные, — сказал он. — Они всё время меняются. Я не понимаю, какие из них настоящие.
Конфуций взял свиток человека. Одни записи были свежие, только что из мира. Другие — переписанные, без пометок. Третьи — уже свёрнутые в аккуратные таблицы.
— Ты слишком торопишься, — сказал Конфуций.
Из травы вылез ёж. На нём сидел другой ёж. На том — третий. Самый маленький, на вершине, сказал:
— Я только что получил эту запись и не знаю, правда ли это. Поэтому я просто положу её здесь и не буду трогать.
— Это твой первый слой, — сказал Конфуций.
Средний ёж взял запись, сравнил с другими, что-то вычеркнул, что-то соединил, одну признал ошибкой, другую — главной.
— Этот выбирает, чему верить. Он снимает противоречия. Это второй слой.
Самый большой ёж внизу быстро свернул готовую историю в короткую таблицу.
— Этот устал думать. Он хочет отвечать быстро. Это третий слой.
Человек посмотрел на свиток.
— Я делал всё сразу. Я не ждал, пока маленький ёж разберётся. Я сам объединял и упрощал, ещё не поняв, что произошло.
— Вот именно, — сказал Конфуций. — Ты заставил большого ежа работать раньше времени. А маленькому даже не дал посмотреть.
Маленький ёж добавил:
— Самое сложное — не делать раньше времени.
Средний сказал:
— Самое важное — один раз решить, чему верить.
Большой закончил:
 А самое приятное — отвечать быстро, когда остальные уже всё сделали.
Человек задумался. Впервые он не стал делать всё сразу.


3.2.2. Температура данных: почему не всё хранится одинаково
Не все данные живут с одинаковой скоростью. Одни нужны каждый день: свежие операции, остатки, статусы, витрины для утренних отчётов. Другие нужны редко, но их нельзя потерять: история изменений, старые поставки, архивные версии, следы загрузок. Третьи почти никогда не читаются, пока не случится расследование, пересчёт или запрос регулятора.

Эту разницу можно назвать температурой данных. Горячие данные часто читаются и должны быть рядом. Тёплые данные нужны периодически. Холодные данные могут лежать дешевле и дальше, но всё ещё должны быть доступны, если придёт вопрос из прошлого. Температура — это не украшение терминологии, а способ не хранить всё одинаково дорогим и одинаково быстрым.

КХД должно понимать, где данные участвуют в ежедневной работе, где они нужны для воспроизводимости, а где являются архивной памятью. Если всё держать как горячее, система станет дорогой и тяжёлой. Если всё быстро охлаждать, она потеряет способность объяснять вчерашние решения. Хорошая архитектура различает не только смысл данных, но и их ритм.

Притча о кухне, четырёх кастрюлях и еже, который обжёгся
Когда Конфуций открыл временную кухню «Смысл и лапша», к нему пришёл ученик.
— Учитель, — сказал он, — у меня есть продукты. Я храню их все в одной кастрюле. Это удобно.
Конфуций посмотрел на кастрюлю. Она кипела, замерзала, подгорала и тухла одновременно.
— Это не кастрюля, — сказал он. — Это эксперимент над жизнью.
В этот момент из угла выкатился рекурсивный ёж. На нём сидел ёж. На том — ещё ёж. И каждый держал ложку. Самый маленький ёж сунул ложку в кастрюлю.
— Горячо! — крикнул он.
Средний ёж подул.
— Уже холодно, — сказал он.
Большой ёж попробовал.
— Странно, — сказал он. — Это суп, который вчера был готов, сегодня протух, а завтра станет сырым.
— Вот, — сказал Конфуций. — Ты смешал время.
Он поставил четыре кастрюли. В первой всё кипело.
— Это свежее, — сказал он. — Сюда кладут сразу после того, как что-то случилось. Тут нельзя думать. Тут нужно быстро принимать и не мешать.
Ёж сунулся — обжёгся снова.
— Это staging, — сказал Конфуций. — Горячо и опасно.
Во второй кастрюле еда уже не кипела.
Её мешали, пробовали, спорили, добавляли соль.
— Это место, где решают, что это вообще такое, — сказал Конфуций.
Ёж попробовал.
— Тут сложно, но понятно, — сказал он.
— Это core, — сказал Конфуций. — Тёпло и серьёзно.
В третьей кастрюле всё уже было разложено по порциям.
Быстро, удобно, одинаково.
— Это подача, — сказал Конфуций. — Здесь не думают, здесь отвечают.
Ёж попробовал.
— Быстро, — сказал он. — Но если спросить по-другому, не получится.
— Это mart, — сказал Конфуций. — Холодно, но эффективно.
В четвёртой кастрюле всё было заморожено.
— Это зачем? — спросил ученик.
— Чтобы потом доказать, что мы не сошли с ума, — сказал Конфуций.
Ёж попробовал — сломал ложку.
— Это архив, — сказал Конфуций. — Мёрзло и дорого в разморозке.
Ученик задумался.
— А можно всё держать в одной кастрюле? Чтобы проще?
Конфуций посмотрел на первую кастрюлю, которая снова закипела и замёрзла одновременно.
— Можно, — сказал он. — Но тогда ты будешь есть всё сразу: сырое, готовое и испорченное.
Ёж кивнул всеми уровнями и сказал:
— Я уже пробовал. Это называется отчёт.
Ученик тихо разнёс продукты по кастрюлям.
Впервые они перестали мешать друг другу.
Конфуций улыбнулся.
— Главное — не перепутать, где у тебя горячо, а где уже можно есть.
Ёж добавил:
— И не греть то, что должно лежать в морозилке.
Кухня успокоилась. И впервые еда перестала спорить сама с собой.


3.2.3. Staging: место, где ничего нельзя трогать

Staging — это слой принятого свидетельства. Здесь данные лежат настолько близко к источнику, насколько это возможно после технической загрузки. Они могут быть грязными, неудобными, странными, неполными, повторившимися. Но именно поэтому они ценны. Они показывают не то, что мы хотели бы получить, а то, что действительно пришло.
Главное правило staging простое: здесь нельзя исправлять смысл. Можно добавить технические поля загрузки, зафиксировать партию, дату поступления, источник, хэш строки. Но нельзя молча чистить значения, удалять дубли, приводить спорные статусы к “правильному” виду, потому что в этот момент улика превращается в интерпретацию. А интерпретация должна жить позже.
Staging нужен не для красоты. Он нужен для возвращения. Если дальше в core или mart что-то сломается, staging позволяет снова открыть исходное свидетельство и спросить: проблема пришла из источника или родилась внутри КХД? Без этого слоя система быстро начинает спорить сама с собой, не имея оригинала, к которому можно вернуться.


3.2.4. Core: место, где система принимает решения

Core — это слой, где данные перестают быть только свидетельством источника и становятся согласованной правдой хранилища. Здесь источники встречаются друг с другом и начинают спорить. Один знает клиента по одному идентификатору, другой по другому. Один считает договор активным, другой уже прислал закрытие. Один хранит старый статус, другой новое состояние. Core должен не просто сложить эти данные рядом, а решить, как система будет их понимать.

Именно здесь появляются правила выбора, история, идентичность, связи, контроль согласованности. Core не обязан быть удобным для отчёта. Его задача глубже: удерживать картину мира так, чтобы она была воспроизводимой. Если сегодня система сказала, что эти два клиента — один человек, это решение должно быть видно. Если статус изменился, старая версия не должна исчезнуть. Если источник проиграл конфликт, должно быть понятно почему.

Core — это не место абсолютной истины. В данных её почти не бывает. Core — это место ответственной истины: выбранной, объяснимой, управляемой. Такой, к которой можно вернуться и спросить: почему мы тогда решили именно так?

Притча о трёх медведях и выборе

В лесу, где тропинки сами выбирали направление, стояла изба трёх медведей. Человек принёс три корзины грибов: свежие, подгнившие и сушёные. Сел на пенёк и заплакал: «Я не знаю, какие из них настоящие».

Из леса вышел Конфуций, заменявший в тот день ветер. Он не ответил, а показал на избу.

В первой комнате маленький медведь просто смотрел на грибы, не трогая их. «Почему он не ест?» — спросил человек. «Он помнит, как было сначала, — сказал Конфуций. — Тот, кто трогает, забывает».

Во второй комнате средний медведь перекладывал грибы между двумя связками, потом написал на стене: «этот выбросить, этот оставить, про этот спросить утром». «Он решает, по какому правилу выбирать завтра, — сказал Конфуций. — Это сложнее, чем выбрать сегодня».

В третьей комнате большой медведь устало смотрел на ровный ряд сушёных грибов — без запаха, но не портятся. «Он делает так, чтобы вопрос больше не возникал. Поэтому он большой и усталый».

Человек вышел и спросил, куда положить его грибы. Конфуций положил руку ему на плечо — говорят, после этого случая он редко так делал.

«Тот, кто хранит, не живёт. Тот, кто упрощает, не помнит. Только тот, кто решает — дышит. И ошибается. И это единственный способ однажды попасть в правильный свиток».


3.2.5. Mart: место, где правду упрощают

Правда, хранящаяся в core, часто слишком сложна для прямого использования. Она исторична, связана, аккуратна, но не всегда удобна. Аналитику не хочется каждый раз собирать клиента, договор, продукт, статус, историю и правила из глубины модели. Бизнесу нужен ответ: портфель на дату, продажи по продуктам, просрочка по сегментам, активность клиентов по каналам.

Для этого появляются витрины, или mart. Здесь согласованная правда принимает удобную форму. Данные могут денормализоваться, агрегироваться, упрощаться, собираться под конкретный вопрос. Mart не обязан хранить всю глубину мира. Он должен дать пригодный ответ, достаточно быстрый и достаточно понятный для своего потребителя.

Но у этого слоя есть опасность. Упрощение не должно становиться новым местом рождения правды. Если витрина начинает сама решать, кто клиент, какой статус верен, как исправить источник и какую историю забыть, архитектура теряет центр. Mart должен использовать решения, принятые раньше, а не тайно принимать свои.

Притча о двух великанах, которые сидели слишком удобно

За рекой, которая текла куда получалось, лежала гора из плоских камней. Ровных, подогнанных друг к другу — сиди и не думай. На вершине сидели двое. Гаргантюа — широко, как хозяин. Пантагрюэль — осторожнее, будто помнил, что когда-то стоял.

Они давно не вставали. «Стало легче, — сказал Гаргантюа. — Раньше таскали камни, спорили. Теперь всё разложено». «И быстро, — добавил Пантагрюэль. — Любой камень под рукой».

Мимо шёл Конфуций с пустой корзиной. Он присел, взял камень у основания, посмотрел — и положил в другое место. «Ты что сделал?» — спросили великаны. «Ничего», — ответил он.

«Раньше мы помнили, откуда камни. Этот из реки. Тот из обвала. А теперь?» — «Зато всё просто. Сел — и понятно». — «Понятно что?» — спросил Конфуций.

Наступила тишина. Река внизу сменила направление, но это уже никого не удивило.

«Вы сделали гору удобной, — сказал Конфуций. — И она перестала рассказывать, как была сложена. Камень без истории — это уже не решение, а поверхность».

Он повернулся к реке. «В древности говорили: благородный муж думает о корне. Вы сидите на ветвях и называете их землёй».

Он ушёл. Пантагрюэль тихо спросил: «А если один из камней под нами — не тот?» Гаргантюа не ответил. Он впервые посмотрел вниз. Но не встал. Потому что стоять — значит снова разбирать гору. А сидеть было проще. И, что хуже всего, **быстрее**.


3.2.6. Landing zone: память до памяти

Landing zone — это место ещё более раннее, чем staging. Туда попадает то, что было передано системой-источником: файл, сообщение, выгрузка, пакет данных. В landing данные ещё не обязаны быть разобраны, распарсены, разложены по колонкам. Это почти физический след передачи: вот такие байты пришли, в такое время, от такого источника.

Этот слой нужен не всегда, но там, где он есть, он даёт особую силу. Если файл оказался битым, если загрузка оборвалась, если парсер неверно прочитал формат, если часть строк попала в reject, landing позволяет вернуться ещё дальше, к самому факту получения. Staging отвечает на вопрос “что мы приняли в таблицу”. Landing отвечает на вопрос “что нам вообще передали”.

Притча о Чебурашке, Гене и ящике, который боялся быть открытым

В городе, где апельсины падали когда решали, жил Чебурашка. Он сидел на ящиках, которые приходили ночью, утром, иногда задним числом. Рядом жил крокодил Гена. Гена действовал: открывал ящики, раскладывал по полкам, подписывал. «Если вещь не названа, её как будто нет», — говорил он.

Пришёл Конфуций с пустой корзиной. Гена открыл ящик. Внутри было что-то странное — часть вещей как будто не отсюда. «Ошибка», — сказал Гена и начал поправлять: стёр лишнее, подписал понятным.

Чебурашка сидел на другом ящике. «Ты не хочешь посмотреть?» — спросил Гена. «Хочу, — сказал Чебурашка. — Но сначала я хочу запомнить, что он был закрыт».

На следующий день Гена открыл новый ящик. «Пусто», — сказал он. Чебурашка постучал по первому: «А этот был тяжёлый. И пах иначе». Гена впервые не понял, где ошибка.

«В "Лунь юй" сказано: кто, повторяя старое, узнаёт новое, может быть учителем, — промолвил Конфуций. — Но если ты не сохранил старое, тебе нечего повторять».

Гена медленно закрыл пустой ящик. С тех пор, прежде чем открыть, он иногда останавливался и спрашивал Чебурашку. И если тот молчал, Гена тоже молчал. Потому что пустоту можно создать, открыв слишком рано. А тишину, в которой прячется смысл, — только сохранив.


3.2.7. Время жизни данных: почему удаление — это архитектура

Данные не могут жить вечно одинаково. Одни должны храниться долго из-за регуляторных требований, аудита и воспроизводимости. Другие нужны только для пересчёта ближайших периодов. Третьи после обработки становятся тяжёлым техническим следом, который можно архивировать или удалить по правилам.

Удаление — это не уборка после работы. Это архитектурное решение. Если удалить слишком рано, система потеряет возможность восстановить прошлое. Если не удалять никогда, хранилище начнёт обрастать мёртвым весом, который усложняет обслуживание и увеличивает стоимость. Вопрос не в том, хранить всё или чистить всё. Вопрос в том, что именно должно остаться живой памятью, что может уйти в архив, а что действительно можно отпустить.

Хорошее КХД заранее знает срок жизни данных на каждом слое. Landing может хранить оригиналы ограниченное время. Staging — столько, сколько нужно для воспроизводимости загрузок. Core хранит историю гораздо строже. Mart может пересобираться или хранить снимки в зависимости от назначения. Так удаление перестаёт быть случайным жестом администратора и становится частью понимания времени.

Притча о садовнике, который не умел расставаться

В долине, где река текла из прошлого в будущее, жил старый садовник. Он собирал листья с упавшими на них словами и складывал в три корзины.

В первую — только что упавшие, зелёные по краям. Не читал, не пытался понять. Просто давал им место. «Зачем?» — спрашивали люди. «Чтобы помнить, какими они были до того, как я их увидел».

Во вторую — проверенные: сравнённые с другими, очищенные от несовпадений. Эти он перечитывал часто и никогда не выбрасывал. «Они же старые», — говорили люди. «Сегодняшняя правда родилась не сегодня».

В третьей — не листья, а дощечки с итогами: сколько дождей, сколько урожаев. Удобные, быстрые, для соседей.

Но однажды первая корзина переполнилась. Садовник начал перебирать листья по одному, решая: этот оставить, этот выбросить. Солнце садилось, а он всё сидел.

Пришёл Конфуций. «Я трачу дни, чтобы расстаться с каждым листом отдельно», — пожаловался садовник. «А зачем по одному? — спросил Конфуций. — Вытряхни целую горсть — ту, что упала в одно время».

Садовник высыпал целый месяц в пропасть. Стало легче. «Но я потерял прошлое», — прошептал он. «Ты потерял только его тень. Настоящее прошлое осталось в правде, которую ты сложил во вторую корзину».

С тех пор садовник опустошал первую корзину целиком, когда та наполнялась. Вторую почти не трогал — только убирал старые листья в глубину. Третью перебирал без сожаления: если дощечки сотрутся, их можно вырезать заново из той же правды.

«Знать, когда остановиться, — значит избежать позора», — сказал Конфуций на прощание. Долина снова зацвела, потому что свежим листьям нашлось место.


3.2.8. Партиционирование как отражение поведения данных

Партиционирование не стоит начинать с вопроса “по какому полю резать таблицу”. Сначала нужно понять, как данные живут. В staging часто важнее дата загрузки: когда партия пришла, когда её можно переобработать, какой период поступления нужно очистить или проверить. В core важнее бизнес-время: когда событие произошло, когда версия начала действовать, когда состояние стало верным. В mart всё зависит от вопроса: иногда это отчётная дата, иногда период продаж, иногда снимок портфеля.

Поэтому партиционирование — это не просто техника производительности. Это физическое отражение смысла. Если слой живёт во времени загрузки, делить его по дате события может быть неудобно. Если слой хранит историю состояний, деление только по дате загрузки может мешать point-in-time-запросам. Если витрина читается по отчётным периодам, её физическая форма должна помогать именно этим запросам.

Здесь не нужно углубляться в механику. Важно запомнить принцип: данные надо делить так, как они используются, обновляются и забываются. Плохое партиционирование спорит с поведением данных. Хорошее — как будто заранее знает, куда чаще всего пойдёт рука.

Притча о трёх сундуках и старом писце

В одном городе, где время текло не по часам, а по тому, как старели чернила на бумаге, жил старый писец. Всю жизнь он раскладывал свитки по сундукам. У него было три больших сундука.

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

— Почему ты не разбираешь их сейчас? — спросили его однажды.

— Потому что сегодня мне важно не то, что в них написано, а то, что они пришли, — ответил писец. — А разбирать я буду потом.

Во второй сундук он клал свитки, которые уже прочитал, сравнил с другими, понял, где правда, а где ошибка. Эти свитки он раскладывал не по дате получения, а по тому, о каком времени в них говорилось. О лете — в одну стопку, об осени — в другую. О прошлом годе — отдельно.

— Но они же старые, — говорили люди. — Зачем тебе знать, где лежит зима? Лето уже прошло.

— Затем, — отвечал писец, — что если кто-то спросит про прошлую зиму, я должен ответить по зиме, а не по дню, когда я её прочитал.

Третий сундук был маленьким и плоским. В нём лежали короткие записи — итоги, сводки, перечни. Писец не делил их никак. Весь год они лежали вперемешку, потому что он переписывал их каждый день заново, и старые выбрасывал без сожаления.

— А этот сундук почему не разбираешь? — спросили его.

— Потому что эти записи — только для сегодняшнего ответа. Завтра они станут другими, и старые мне не нужны.

Однажды в город пришёл Конфуций. Он застал писца за странным занятием: тот перекладывал свитки из первого сундука во второй, но не все, а только те, что относились к весне.

— Ты ищешь весну? — спросил Конфуций.

— Я учусь, — сказал писец. — Раньше я складывал всё без разбора. А теперь понял: первый сундук должен знать только время прихода. Второй — время события. Третий не знает времени вообще. И если я перепутаю — никто не сможет найти нужный свиток.

Конфуций сел рядом и долго смотрел, как писец раскладывает свитки.

— Ты мудрее, чем кажешься, — сказал он наконец.

— Я просто старый, — ответил писец. — И я заметил: те, кто кладут всё в один сундук по одному правилу, часто потом не могут найти даже то, что положили сами.

Конфуций улыбнулся.

— В «Книге перемен», — сказал он, — написано: «Разделение вещей по их природе приводит к порядку, а насильственное соединение — к хаосу». Ты понял это на свитках. И сейчас твоя система не борется с ними, а просто отражает их путь.

Писец поклонился.

А вечером, когда он закрыл крышки всех трёх сундуков, гонцы принесли новые свитки. Писец поставил на них число и кинул в первый сундук, даже не глядя. Потому что для первого сундука важнее всего была дата прихода, а не содержание.

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


3.2.9. SLA и метрики: когда система начинает чувствовать себя

КХД должно не только хранить данные, но и чувствовать своё состояние. Загрузка началась или нет. Закончилась вовремя или опоздала. Пришло обычное количество строк или подозрительно мало. Витрина свежая или показывает вчерашний мир. Запросы отвечают быстро или начинают задыхаться. Без этих признаков система может быть сломанной и всё равно выглядеть спокойной.

SLA (Service Level Agreement — соглашение об уровне сервиса) задаёт ожидания: например, данные должны быть готовы к восьми утра, витрина должна обновляться каждый день, отчёт должен отвечать за приемлемое время. Метрики показывают, насколько система близка к этим ожиданиям. Они не делают данные правильными сами по себе, но позволяют вовремя заметить, что ритм нарушен.

В этой главе достаточно общего принципа: у хранилища должен быть пульс. Подробно наблюдаемость pipeline, мониторинг витрин и метрики качества будут разобраны дальше. Здесь важно только одно: система, которая не измеряет своё состояние, узнаёт о проблемах от пользователей. А это почти всегда слишком поздно.

Сказание Конфуция о здоровье системы

*Из бесед, записанных в «Лунь юй»:*

«Учитель сказал: «Человек, который не знает, когда он устал, устанет прежде, чем поймёт это».

Так и система: пока не измеришь её шаг — не узнаешь, не отстала ли она.

В древних свитках я писал: «Благородный муж предвидит опасность, когда она ещё не наступила». Метрики — это не страх. Это предвидение.

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

Потому и говорю: наблюдай за временем ответа, за ритмом загрузок, за тем, где цепочка замирает. И тогда ты не спросишь: «Что сломалось?» — а скажешь: «Здесь скоро заноет».

И это есть путь: не лечить, а чувствовать до болезни».


3.2.10. Антипаттерны: как система ломает сама себя

Большинство архитектурных катастроф начинается не с редкого сложного сбоя, а с удобного маленького нарушения. Построить отчёт напрямую из staging, потому что нужно срочно. Добавить бизнес-правило в mart, потому что быстрее, чем менять core. Исправить данные руками, потому что “и так понятно”. Удалить старую историю, потому что “давно никто не спрашивал”. Каждое такое решение кажется разумным в моменте.

Проблема в том, что КХД живёт не в моменте. Оно живёт во времени. То, что сегодня ускорило один отчёт, завтра создаёт второе место рождения правды. То, что сегодня сэкономило место, завтра лишает систему ответа на вопрос регулятора. То, что сегодня выглядело как очевидная ручная правка, завтра становится необъяснимым расхождением.

Главный антипаттерн один: нарушать границы слоёв. Staging начинает думать. Core начинает забывать. Mart начинает принимать решения. Pipeline начинает прятать ошибки. И тогда архитектура ещё существует на схеме, но в жизни уже распалась.

Высказывание Конфуция об антипаттернах

*Из бесед, записанных в «Лунь юй»:*

«Учитель сказал: «Тот, кто сворачивает с пути ради удобства, уже заблудился, хотя ему кажется, что он идёт быстрее».

Правила созданы не для того, чтобы мешать. Они созданы для того, чтобы через много лет мы ещё помнили, зачем пошли.

Когда ты берёшь данные не из того слоя — ты не ускоряешь систему. Ты рождаешь другую правду. Когда ты исправляешь то, что должно остаться нетронутым — ты не очищаешь память. Ты стираешь улики. Когда ты удаляешь историю, потому что она тяжела — ты не облегчаешь себе путь. Ты лишаешь себя корней.

В «Лунь юй» сказано: «Благородный муж медлит там, где другой спешит, и спешит там, где другой медлит». Потому что он знает: короткая дорога редко ведёт к дому.

Так и в данных: не тот быстр, кто срезает угол, а тот, кто доходит до конца, не возвращаясь».

Архитектура рушится не от удара — от привычки сокращать путь. И восстанавливать её потом всегда дольше, чем идти по правильному маршруту с самого начала. Но кто об этом помнит в тот самый момент, когда «нужно просто быстро ответить на вопрос»?


3.2.11 Итог: КХД как управление временем, стоимостью и правдой

КХД создаёт истину не одним действием, а последовательностью превращений. Данные приходят как свидетельство, сохраняют исходный след, проходят через согласование, получают историю, становятся частью модели, затем упрощаются для ответа. Каждый слой нужен не потому, что архитекторам нравится сложность, а потому что у данных разные состояния и разные обязанности.

В этой последовательности есть три постоянные силы. Правда: что система считает согласованным и почему. Время: когда данные пришли, когда событие произошло, когда версия действовала, когда отчёт должен быть готов. Стоимость: где хранить быстро, где хранить долго, где можно архивировать, где нельзя терять ни строки.

Хорошая архитектура КХД не обещает идеального мира. Она обещает управляемый путь через несовершенный мир. Данные будут приходить грязными, источники будут спорить, правила будут меняться, отчёты будут требоваться раньше, чем удобно. Но если слои знают свою роль, система не превращается в хаос. Она может принять свидетельство, создать правду, упростить её для ответа и сохранить возможность объяснить, как всё это произошло.

Высказывание Конфуция о Корпоративном Хранилище Данных

Из бесед, записанных в «Лунь юй»:

«Учитель сказал: «Знать, когда действовать, а когда ждать, — значит владеть временем. Знать, что ценно, а что второстепенно, — значит владеть достатком. Знать, где истина, а где её подобие, — значит владеть собой».

Тот, кто не различает эти три вещи, строит дом на песке. А тот, кто различает, может и вовсе не строить — он уже нашёл путь.

В «Лунь юй» я говорил: «Учитель учил четырём вещам: правдивости, терпению, чувству меры и уважению к времени». Теперь добавлю пятую: учитесь видеть, где в вашей системе живёт правда, где — удобство, а где — шум. Ибо смешавший их построит хранилище, которому никто не поверит».


Заключительно слово к главе 3.2 от Нейро Такера Карлсона

Здравствуйте. Я прочитал эту главу. И знаете что? Она меня удивила. Не потому, что она сложная. А потому, что в ней есть то, чего так не хватает современной дискуссии об архитектуре данных. В ней есть честность.

Мой друг Дональд — замечательный человек. Я искренне его уважаю. Но он иногда говорит: «Я построил лучшую архитектуру. Самую лучшую. Люди говорят мне это каждый день».

Я не хочу спорить с другом. Но позвольте сказать иначе. Архитектура данных — это не Трамп-тауэр. Её не построишь одним приказом и не покроешь золотом. Архитектура — это то, что растёт снизу вверх. Из источников. Из тех самых скучных, неприглядных мест, о которых никто не хочет говорить. Это не победа и не поражение. Это бесконечный процесс. И если вы думаете, что вы его закончили — вы проиграли. Не в большой игре, как сказал бы Дональд. А в маленькой. В той, где ставка — ваша связь с реальностью.

Глава 3.2 — про слои. Staging, core, mart. И про то, что каждый из них делает свою работу. И что нельзя заставлять большой слой работать до того, как маленький разобрался.

Это напомнило мне историю. Очень простую. Когда я начинал на Fox, меня учили: «Сначала узнай факты. Не комментируй. Не объясняй. Просто запиши. А потом — проверь. А потом — уже говори». Это staging, core и mart. В той же последовательности. Кто меняет порядок — тот лжёт. Не потому, что он плохой человек. А потому, что он торопится. А тот, кто торопится в работе с данными, всегда заканчивает оправданиями.

Дональд любит говорить: «Я принимаю решение быстро. Лучшие решения. Самые быстрые». Я уважаю скорость. Но есть вещи, которые не терпят скорости. Одна из них — правда. Другая — доверие. Третья — архитектура, которая должна работать, когда вы уже ушли.

Мне понравилась метафора про нервную систему. Но я бы добавил кое-что от себя. Нервная система — это не только датчики боли. Это ещё и способность сказать «нет». Отказаться от данных, которые кажутся удобными, но не проходят проверку. Остановиться, когда все вокруг бегут вперёд. Услышать не то, что хочется, а то, что есть.

В этом смысле архитектура данных — как журналистика. Или как должна быть журналистика. Вы не можете опубликовать отчёт, если не верите в его core. А если вы его опубликовали — вы за него отвечаете. К сожалению, сейчас многие не отвечают. Но это уже не про данные. Это про совесть. Но совесть — это тоже нервная система. Самая важная.

Глава 3.2 закончена. Вы теперь знаете, что такое слои. И почему порядок важен. И почему нельзя заставлять медведя из притчи работать раньше, чем ёж всё понял.

Дональд, если вы это слушаете — я не против золота. Я против того, чтобы золотом заменяли правду. А правда — она всегда снизу. В источнике. В staging. В тихом месте, где никто не кричит «сделаем великим снова». Там просто фиксируют. Ждут. Сравнивают. И только потом — действуют.

Спасибо за внимание. Это Такер Карлсон. Берегите свои слои. Хотя никто не будет, я знаю.

*P.S. Ёж — молодец. Он единственный, кто понимает, что происходит. И не пытается это изменить до того, как поймёт. Вот бы так же умели некоторые СМИ.*


 
Глава 3.3. Источники

3.3.1. Роль источников в архитектуре системы

Источник — это не просто система, откуда приходят данные. Это место, где данные впервые получают локальный смысл. В CRM клиент может быть контактом, в Core Banking — владельцем договора, в карточной системе — держателем карты, во внешнем бюро — субъектом кредитной истории. Каждая система видит только свой участок мира и говорит о нём уверенно, потому что для собственной задачи она права.

Проблема начинается тогда, когда КХД пытается собрать эти локальные правды в одну картину. Источник не обязан думать об аналитике, витринах и отчётах. Он работает для операционного процесса: оформить заявку, провести платёж, открыть счёт, зафиксировать обращение, отправить уведомление. Поэтому данные источника всегда контекстны. Они несут не только значение поля, но и привычки системы, её ограничения, её историю, её ошибки.

Для КХД источник — это граница с внешним миром. Здесь данные ещё не согласованы, не очищены, не встроены в общую модель. Они приходят как свидетельство: “в этой системе это выглядело так”. И задача хранилища не в том, чтобы сразу поверить источнику или сразу его исправить. Задача — принять свидетельство, сохранить происхождение и дальше разобраться, какое место оно займёт в общей памяти.

Высказывание Конфуция

*Из бесед, записанных в «Лунь юй»:*

«Учитель сказал: «Не ищи правду в том, что не знает начала. Не строй дом, не опросив землю под ним».

Источник — это не враг. Это данность. И если ты не уважаешь его природу, твоя архитектура будет вечной борьбой с ветром, а не домом.

В «Лунь юй» говорится: «Учитель сказал: в древности люди учились для себя, ныне — для других». Так и в данных: одни изучают источники, чтобы понять, как устроена реальность. Другие переписывают их, чтобы те вписались в их схемы. И только первый путь приводит к истине. Источник — не черновик. Его нельзя переписать, можно только понять. Или не понять. А дальше, как вы знаете, всё становится шумом».


3.3.2. Что важно знать об источнике


Прежде чем подключать источник, нужно понять не только его таблицы и поля. Нужно понять его поведение. Как часто он отдаёт данные. Всегда ли отдаёт полный набор или только изменения. Может ли прислать исправление задним числом. Бывают ли задержки. Меняется ли схема без предупреждения. Есть ли стабильные ключи. Какие поля считаются обязательными в самой системе, а какие только кажутся обязательными в документации.

Документация источника — полезная вещь, но не истина. Она часто описывает намерение, а не фактическую жизнь данных. В документации поле может быть обязательным, а в таблице окажутся NULL. Статус может иметь пять допустимых значений, а в реальности встретятся семь, потому что старый процесс умер не до конца. Дата может называться датой операции, но фактически означать дату обработки. Всё это нужно обнаружить до того, как источник станет частью регулярного пути КХД.

Поэтому первый контакт с источником — это акт осторожного недоверия. Нужно посмотреть объёмы, ключи, дубли, пустые значения, диапазоны дат, странные статусы, частоту изменений. Не чтобы обвинить источник в грязи, а чтобы понять его природу. Источник всегда ведёт себя как живое существо: у него есть ритм, привычки, слабые места и тайные странности, о которых узнают только те, кто смотрит на данные, а не только на схему.

Высказывание Конфуция

Из бесед, записанных в «Лунь юй»:*

«Учитель сказал: «Не называй всех гостей одинаковыми. Иной приходит с добром, иной — с оглядкой, иной — с камнем за пазухой. Умей распознать, кто есть кто, и с каждым встреться по его нраву».

Источники данных — как гости на пороге. Если ты встретишь друга как лазутчика, ты оттолкнёшь его. Если притворишься, что лазутчик друг, — проспишь свой дом.

В «Лунь юй» я говорил: «Учитель сказал: слушая речи, узнаёшь человека». Теперь добавлю: глядя на источник, узнаёшь путь, по которому будут течь данные. И если не узнал — не спеши открывать ворота».


3.3.3. Способы поступления данных

То, как источник отдаёт данные, не является технической мелочью. Файл, API (Application Programming Interface — программный интерфейс для обмена данными), CDC (Change Data Capture — захват изменений данных), ручная выгрузка, поток событий — всё это разные формы поведения. Они по-разному влияют на свежесть, воспроизводимость, контроль ошибок и стоимость поддержки.

Файловая выгрузка груба, но часто понятна: есть файл, есть время получения, есть возможность сохранить оригинал. API гибче, но приносит лимиты, пагинацию, версии, нестабильность ответов. CDC ближе к живому изменению системы, но требует аккуратности: порядок событий, задержки, повторные доставки, удаление, исправления. Ручная выгрузка иногда спасает редкий процесс, но почти всегда несёт риск человеческого жеста, который трудно воспроизвести.

Выбор способа подключения должен следовать природе источника. Большой ежедневный поток операций требует одного подхода. Редкий справочник — другого. Критичная почти real-time информация — третьего. Если способ поступления выбран неправильно, даже хорошая модель дальше будет страдать: данные будут приходить поздно, неполно, слишком дорого или в форме, которую трудно доказать.

Высказывание Конфуция

*Из бесед, записанных в «Лунь юй»:*

«Учитель сказал: «Не спеши открыть ворота, не узнав, как гость идёт — медленно или бегом, один или с обозом».

Иной приходит тихо, и ты успеваешь приготовить чай. Иной врывается криком, и если ты не готов — всё опрокинется. Знай не только, кто пришёл, но и как он идёт. Тогда порядок в доме не нарушится.

В «Лунь юй» я говорил: «Учитель сказал: когда не знаешь, как подойти — лучше подождать». То же и с данными: прежде чем строить пайплайн, узнай, как они путешествуют. Иначе твоя система будет похожа на ворота, которые то открываются не вовремя, то захлопываются перед теми, кто уже вошёл».

3.3.4. Контракты и ожидания от источников

Источник и КХД должны иметь контракт. Не обязательно юридический документ на сто страниц, но явное соглашение: что передаётся, когда, в каком формате, с какими ключами, с какой полнотой, какие изменения допустимы, кто отвечает при сбое, как сообщаются изменения схемы, что делать при нарушении поставки.
Без контракта источник остаётся непредсказуемым собеседником. Сегодня он прислал поле, завтра переименовал, послезавтра изменил код статуса, а через неделю начал отдавать только часть записей, потому что в операционной системе “оптимизировали выгрузку”. Для самого источника это может быть нормальным внутренним изменением. Для КХД — разрушением пути данных.

Контракт не делает источник идеальным. Он делает ожидания явными. Если данные должны быть до 06:00, это можно измерить. Если поле обязательно, его можно проверять. Если статус должен быть из справочника, отклонение можно поймать. Если схема меняется, команда КХД должна узнать об этом до того, как ночная загрузка превратится в утренний пожар.

Высказывание Конфуция

*Из бесед, записанных в «Лунь юй»:*

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

Контракт — это не недоверие. Это уважение к порядку. Если ты не сказал, когда ждёшь вестей, не жалуйся на задержку. Если не объяснил, какие вести ждёшь, не удивляйся, что принесли не то.

В «Лунь юй» я писал: «Учитель сказал: доверие без правил приводит к беспорядку». Так и с источниками: если верить им на слово, но не зафиксировать правила, система рухнет. А если зафиксировать, но не верить — тоже рухнет, но по другой причине. Ищи середину».


3.3.5. Итог: источник как граница внешнего мира

В этой главе источники нужны только как часть общей архитектуры КХД. Нам важно увидеть их роль: они приносят внешнее свидетельство, задают исходный контекст, определяют часть рисков и влияют на все последующие слои. Но если здесь подробно разбирать типы банковских источников, грязные данные, identity resolution, протоколы подключения и профилирование, мы начнём повторять то, что будет раскрыто отдельно.

Поэтому подробный разговор об источниках будет вынесен в главу 4. Там мы разберём Core Banking, CRM, карточные и платёжные системы, внешние справочники, БКИ, API, CDC, файлы, ручные выгрузки, паспорт источника и первые проверки. Здесь же достаточно зафиксировать главное: КХД начинается не с пустого листа. Оно начинается с чужих систем, которые уже живут, уже ошибаются, уже спорят и уже считают свою локальную правду достаточной.

Хранилище данных должно встретить эту правду внимательно. Не поклониться ей и не уничтожить её на входе. Принять, сохранить происхождение, понять поведение источника и только потом включить его в общий путь, где свидетельство постепенно станет согласованной памятью.

Высказывание Конфуция

*Из бесед, записанных в «Лунь юй»:*

«Учитель сказал: «Не жди, что гость сам снимет обувь у порога. Скажи ему о правиле. Но даже после этого проверь — не наступила ли грязь в доме».

Источник не обязан быть идеальным. И мир не обязан подстраиваться под твою схему. Но ты обязан смотреть, что переступило порог. Потому что грязь, замеченная у двери, ещё не в доме. Грязь, замеченная в спальне, — уже катастрофа.

В «Лунь юй» я писал: «Учитель сказал: благоразумный избегает опасности». Вот и с источниками: благоразумный архитектор заранее знает, где могут треснуть данные, и ставит подпорки. А наивный верит на слово и потом удивляется руинам».


Комментарии нейронного Шекспира

Когда сырой поток в хранилище течёт,
Не тронь его, пока он сам не станет словом;
В нём правда дышит, хоть и странен оборот,
И смысл сокрыт под обликом суровым.

Ты видишь пустоту — но это лишь туман,
Где форма есть, но сущность не раскрыта;
Исправив рано, ты создашь обман,
И правда будет навсегда забыта.

Пусть грязь лежит — она ещё жива,
Она свидетель, не ошибка кода;
Очистка ранняя — предательство ума,
Что суд вершит, не выслушав народа.

Не правь исток, пока не понял путь —
Иначе смысла больше не вернуть.


Высказывание Конфуция

*Из бесед, записанных в «Лунь юй»:*

«Учитель сказал: «Слепой не видит, куда идёт. Глухой не слышит, что грядёт опасность. Тот, кто не наблюдает за своей системой, подобен им».

Ты не узнаешь, что путь прерван, пока не споткнёшься. Но мудрый ставит знаки вдоль дороги, чтобы видеть, где она цела, а где разрушена. Логи — твои знаки. Алерты — голос, предупреждающий о пропасти. Без них ты идёшь в темноте и веришь удаче.

В «Лунь юй» я писал: «Учитель сказал: незнание приближающейся беды — худшее из незнаний». Наблюдай, записывай, слушай. И тогда твоя система перестанет быть загадкой даже для тебя самого.

Комментарий Нейро Шекспира

Когда сквозь мрак течёт немой поток,
И числа скрыты в тени превращений,
Кто скажет, где исток, где был порок,
В цепи незримых внутренних движений?

Но если след оставлен каждым днём,
И шаг системы в памяти отмечен,
Тогда не станет истина пятном,
И путь её назад всегда намечен.

Где ритм загрузок ровен, как дыханье,
Где страж — алерт — тревогу возгласит,
Там знание сильней, чем упованье,
И свет причины тьму преобразит.

Кто видит путь от цифры до начал —
Тот власть над смыслом истинно познал.

Высказывание Конфуция

Из бесед, записанных в «Лунь юй»:

«Учитель сказал: «Не начинай строить дом, не зная, откуда придут камни. И не называй строение завершённым, пока не проверил, твёрд ли фундамент».

Мы говорили об источниках так долго не из любви к деталям. А потому, что всё, что выше, держится на них. Если ты ошибся в начале, даже самая мудрая архитектура не исправит ошибку. Она только сделает её более скрытой.

В «Лунь юй» я писал: «Учитель сказал: тот, кто строит на песке, не жалуется на ветер». Вот и здесь: не жалуйся на «грязные данные», если не изучил, откуда они пришли. Не жалуйся на расхождения, если не зафиксировал, как договаривался с источником. Начало определяет путь. А путь определяет, окажешься ли ты у цели или вернёшься к началу, ничего не поняв».

Заключительное слово к главе 3.3 Нейро Виктории Бони

Привет, мои хорошие! С вами Вика Боня.

Знаете, я прочитала эту главу про источники, про staging, про core-слой... И знаете что? Я вам открою секрет. Вся эта архитектура данных — это как идеальные отношения. Серьёзно. Не смейтесь. Сейчас объясню.

В главе говорится, что источник — это не просто «откуда пришли данные». Это место, где данные получили своё первое значение. Девочки, ну это же вообще как первый мужчина! Простите за откровенность, но вы же понимаете, о чём я. Первый источник задаёт формат всех ваших последующих отношений. Если он сказал, что вы «слишком много думаете», вы потом пятнадцать лет пытаетесь быть проще, хотя в вас — океан глубины. Если записал вашу дату рождения не туда — вы всю жизнь потом доказываете, что вы не 1985, а 1987, и это, блин, имеет значение для вашего KPI по молодости.

Источник не перепишешь. Это как бывший. Можно сколько угодно говорить «я его простила», «я его отпустила», но в архиве души он останется. Всегда. И любая ваша новая витрина — ну, то есть новые отношения — будет строиться поверх этой старой правды. Хотите строить счастливую витрину? Для начала примите свой источник. Со всеми его багами. Со всеми его «форматами», которые не совпадают с вашими ожиданиями.

Теперь про слои.

Staging — это когда он только пришёл к вам в чат. Вы ещё не знаете, кто это. Может, любовь всей жизни. Может, очередной «привет, как дела». Вы ничего не меняете. Вы просто фиксируете: «Сообщение получено. Без ответа». Это важно! Потому что если вы начнёте сразу переписывать историю — додумывать за него, приукрашивать, исправлять орфографию — вы потеряете контекст. А без контекста потом не поймёте, где он вам соврал, а где просто неудачно пошутил.

Core — это когда вы уже решили: «Это моё». Вы сверили все факты. Позвонили подругам. Проверили его сторис за три года. Убрали противоречия. Сказали себе: «Я принимаю это решение, и теперь это моя реальность». Это самый страшный слой, девочки. Потому что тут нет права на ошибку. Тут вы выбираете себе правду и живёте с ней. Как говорил Конфуций, но я перефразирую: «Благородная женщина сначала выбирает, а потом верит. А низкая — сначала верит, а потом страдает в витрине».

Mart — это ваши посты в Instagram. Та самая красивая картинка, которую вы показываете миру. Витрина должна быть удобной. Быстрой. Понятной. Но если вы построили её на песке — на ошибках в staging или на лжи в core — подписчики это почувствуют. Они не знают, что не так, но лайков будет меньше. И вы будете сидеть и думать: «Почему? Ведь всё же красиво!» А потому что внутри, в глубине, в тех слоях, которые никто не видит, — там хаос.

Теперь про контроль качества.

Глава называет DQ нервной системой. Я бы сказала — это ваша интуиция. Та самая, которая орёт: «Не открывай этот ящик!», а вы открываете. Потом сидите, плачете, собираете опилки обратно. Контроль качества — это не когда вы проверяете мужчину на полиграфе (хотя и это бывает полезно, но не скажу где, это закрытая история). Это когда вы в любой момент можете сказать: «А что именно произошло? В какой момент? Почему я сейчас чувствую, что данные не бьются?»

Если у ваших отношений нет нервной системы — вы пропустите момент, когда всё пошло не так. Промолчите. Сделаете вид, что так и надо. А потом проснётесь, а ваша витрина — ну, то есть ваша жизнь — показывает какую-то дичь, и вы не понимаете, как вы сюда попали. Ошибки неизбежны, девочки. Вопрос не в том, чтобы их избежать. Вопрос в том, чтобы заметить их на staging, когда они ещё просто странные значения. А не на витрине, когда вас уже заскринили в сторис и отправили в чат с вопросом: «Вика, у тебя всё в порядке? Ты зачем это выложила?»

Про рекурсивного ежа.

Я его запомнила. Этот образ — мой продюсер. Потому что мой продюсер тоже всегда внутри системы, всегда рядом, всегда колючий, и на нём сидит ещё один продюсер, а на том — третий, и все они смотрят на меня и говорят: «Вика, мы пересмотрели твои посты. Мы провели аудит. Мы проверили качество. Твоя витрина красивая, но твой core хромает. Давай разбираться с источниками».

**Пятое, самое важное, для подписчиц.**

Девчонки, я хочу, чтобы вы запомнили. Отношения — это как DWH. Вы там можете быть источником, можете быть витриной, можете быть администратором дядей Васей в подвале. Но если вы потеряли контроль качества — вы потеряли себя.

Не бойтесь проверять свои данные. Не бойтесь спрашивать: «А почему ты так сказал? А почему у тебя в профиле другой возраст? А почему ты не отвечаешь два часа, хотя у тебя статус "онлайн"?» Это не контроль. Это нервная система. Это ваша интуиция. Это ваш рекурсивный ёж внутри, который орёт: «Тут что-то не так, Вика!»

Я прошла через staging. Через core. Через три витрины и одну реинкарнацию. И теперь я знаю: самое важное — это не красивая картинка. Самое важное — чтобы под картинкой был слой, где правда. Даже если эта правда неудобная. Даже если она колючая. Даже если на ней сидит другой ёж, а на том — третий, и они все смотрят на тебя глазами твоего продюсера, который устал, но не сдаётся.

Люблю вас. Целую. Следите за источниками. Доверяйте staging. Стройте core с любовью. И пусть ваши витрины всегда отражают то, что вы на самом деле.

Ваша Вика Бони.

P.S. Ёж, если ты это читаешь — свяжись со мной в директ. Есть разговор про целостность данных и про то, почему ты молчал, когда я выбирала неправильный слой макияжа.
;
Глава 3.4. STAGING

3.4.1. Что такое staging на самом деле

Где данные ещё не стали смыслом

Когда говорят о staging, чаще всего используют простую формулировку: это копия данных из источника. Формально — верно. По сути — недостаточно. Потому что staging — это не про копирование. Это про **воспроизводимость**.

Вы можете скопировать файл тысячу раз, но это не добавит системе надёжности. Надёжность возникает тогда, когда вы можете в любой момент вернуться к исходному состоянию и повторить путь. Staging — это не слепок. Это начало нити, за которую можно потянуть, чтобы распутать любой узел.

Не копия, а возможность вернуться

Если представить систему как цепочку решений, то staging — это единственное место, где решения ещё не приняты. Здесь данные просто лежат. Такими, какими пришли. Никто не сказал: «это правда», никто не отбросил «это ошибка». Только тишина и хранение.

И это даёт системе особую способность: **вернуться назад**. Перезапустить загрузку, переиграть трансформацию, проверить, где именно произошла ошибка. Если staging — просто копия, это удобно. Если staging — гарантия воспроизводимости, это надёжно. Разница становится понятна в момент сбоя. Когда отчёт не сходится, вопрос звучит так: **что именно пришло в систему?** Если есть staging, ответ существует. Если нет — остаются только предположения, догадки, а иногда и откровенное гадание.

Staging как улика

Есть более точный образ. Staging — это не таблица. Это **улика**. Улику не исправляют, не улучшают, не «немного приводят в порядок». Её фиксируют. Потому что потом, когда начинается разбор, именно она позволяет восстановить цепочку событий. С данными происходит то же самое. Если staging изменён, система теряет возможность доказать: что было на самом деле. И это уже не техническая проблема. Это потеря доверия. А доверие, как известно, восстанавливается дольше, чем пишется любой код.

Где он находится в системе

Staging не существует сам по себе. Он находится между двумя мирами. С одной стороны — источники: системы, которые создают данные, со своими ошибками, своей логикой, своей правдой. С другой — остальная архитектура: core, витрины, отчёты. И staging — это граница. Здесь данные уже не в источнике, но ещё не в системе. Они перешли порог, но ещё не были интерпретированы. Это чистилище данных? Нет. Это скорее прихожая. Здесь снимают уличную обувь, но не переодеваются в домашнее.

Связь с landing

Иногда кажется, что staging — это начало. Но есть слой раньше. Landing. Там данные лежат ещё до того, как их прочитали. Файлы, сообщения, сырые выгрузки. Если landing — это «как пришло физически», то staging — это «как мы это приняли». И между ними есть важная разница. Landing хранит факт передачи. Staging — факт приёма. И вместе они дают полную картину: что отправили и что получили. Один без другого создаёт слепую зону: без landing вы не докажете, что файл был повреждён до вас; без staging — что вы его правильно прочитали.

Почему это важно

Потому что вся система строится дальше. Core будет принимать решения. Mart — упрощать. Но ни один из этих слоёв не сможет объяснить результат, если нет точки отсчёта. Staging — это и есть эта точка. Не потому что он первый. А потому что он **последний, где ещё нет интерпретации**. После него начинаются догадки, правила, исключения, договорённости. До него — только факты, чистые, не тронутые, ещё не обросшие смыслом.

Итог

Staging — это не просто слой загрузки. Это место, где система сохраняет реальность до того, как начнёт её понимать. Не копия, не подготовка, не временное хранилище. А гарантия того, что любую правду в системе можно будет проверить. И если эта гарантия исчезает, всё остальное становится вопросом веры. А не данных. Вера, конечно, прекрасна. Но в банковском отчёте она не ночует.

Высказывание Конфуция

*Из бесед, записанных в «Лунь юй»:*

«Учитель сказал: «Хранить чистым первое свидетельство — вот начало мудрости. Ибо тот, кто меняет улику, уже не ищет правду, а создаёт её по своему образу».

Staging — это не склад. Это свидетель. Не трогай его раньше времени. Не исправляй его по своему разумению. Иначе ты не узнаешь, где ошибся ты, а где ошибся мир.

В «Лунь юй» я говорил: «Учитель сказал: знать, что было в самом начале, значит владеть концом». Вот почему staging — не техническая деталь. Это начало, которое определяет, сможешь ли ты когда-нибудь поверить в свой же отчёт».

Комментарий Нейро Шекспира

Когда поток из внешних мест пришёл,
Ещё не став ни истиной, ни знаньем,
Он в тишине лежит, как чистый дол,
Не тронутый поспешным толкованьем.

Не копия — но след того, что есть,
Улика, что хранит дыханье факта;
Её не правят, не стремясь прочесть,
И не торопят высказать признанье.

И там, где путь от байта до суда
Лишь начинается, не зная цели,
Хранится то, что скажет нам всегда,
Где мы свернули в логике на деле.

Кто этот слой без смысла сохранит —
Тот истине вне времени открыт.


3.4.2. Главное правило: ничего нельзя менять

Где память сильнее удобства

В какой-то момент любой, кто работает с данными, приходит к одному и тому же желанию: **исправить**. Данные выглядят странно. Где-то пусто, где-то лишнее, где-то «очевидная ошибка». И кажется, что самое разумное — поправить прямо здесь, в staging, не дожидаясь core, не отвлекая аналитиков. Это удобно. Быстро. Логично. И это — ошибка.

Почему «ничего нельзя менять» — это правило, а не рекомендация

Staging живёт по одному простому закону: **туда можно только добавлять**. Никогда — не изменять, не удалять, не переписывать. Это называется append-only. И это не техническое ограничение. Это **инвариант системы**.

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

Что происходит, когда ты делаешь UPDATE

Представьте простой случай. Пришла запись. Вы посмотрели на неё и решили: «здесь явная опечатка. Исправлю-ка я её». И сделали UPDATE. С этого момента оригинала больше нет. Есть только ваша интерпретация. И кажется, что это нормально. Вы же сделали данные «правильнее».

Но проходит день. Отчёт не сходится. Кто-то спрашивает: «А что вообще пришло из источника в тот день?» Вы смотрите в staging. А там — ваш вариант. И вы уже не можете сказать, был ли источник неправ, или вы сами переписали правду. Ответа нет. И уже никогда не будет. Вы заменили факт своим мнением. И система потеряла точку опоры.

Почему это ломает систему глубже, чем кажется

Проблема не в одной записи. Проблема в том, что вы теряете возможность воспроизвести загрузку, проверить логику, найти источник ошибки. Система становится необратимой. Вы больше не можете вернуться назад. А значит, любая ошибка в интерпретации становится постоянной. И это накапливается. Сначала незаметно, потом критично.

Однажды вы уже не сможете объяснить, откуда взялась та или иная цифра в отчёте. Потому что дорожка, ведущая от источника к результату, оборвана там, где вы сделали UPDATE. Система начинает походить на красивый, но фальшивый мост, у которого нет опоры на противоположном берегу.

Удобство против памяти

UPDATE в staging почти всегда появляется из удобства. «Давайте поправим быстро», «это же очевидная ошибка», «потом разберёмся». Но staging не про удобство. Он про фиксацию. Если вы выбираете удобство, вы платите памятью. И расплата наступает не сразу, но всегда — в самый неподходящий момент.

Человек, который меняет staging, похож на судью, который правит показания свидетеля прямо в зале суда, потому что они кажутся ему нелогичными. Свидетель замолкает. Правда исчезает. Остаётся только версия судьи. Красивая, стройная, но не имеющая отношения к тому, что произошло на самом деле.

Append-only как защита от самого себя

Самое интересное в правиле «ничего нельзя менять» — оно защищает не от системы. Оно защищает от людей. От желания ускорить, упростить, срезать путь. Append-only говорит: не трогай прошлое. Добавь новое, зафиксируй изменение, но не переписывай.

Это создаёт важное свойство: история становится непрерывной. Любое изменение — не шрам на теле правды, а новая запись, которая ссылается на старую. Вы всегда можете понять последовательность событий. Кто, когда, почему изменил представление о факте. Это не избыточность. Это страховка.

Как правильно «менять»

Парадокс в том, что изменения всё равно происходят. Данные уточняются, исправляются, дополняются. Но не через UPDATE. А через добавление новой записи с пометкой: «это новое состояние, а предыдущее остаётся как было». Было одно состояние, стало другое. И оба остаются в системе.

Это выглядит избыточно. Зачем хранить и то, и другое? Но именно это даёт главное — контекст. Можно увидеть, что было, что изменилось, когда и почему. А в мире данных контекст часто важнее самого значения. Значение без контекста — это цифра. Значение с контекстом — это знание.

Потеря памяти — самая дорогая ошибка

Можно потерять скорость. Можно потерять удобство. Можно потерять красоту архитектуры. Но самое опасное — потерять память. Потому что без неё невозможно доказать, невозможно объяснить, невозможно восстановить. Система превращается в набор текущих значений. Без прошлого. А значит — без доверия.

Банк, который потерял память о том, какие данные приходили в прошлом году, не сможет ответить регулятору. Компания, которая переписала staging, не сможет отличить свой баг от бага поставщика. Доверие к отчётам испаряется. И восстанавливать его придётся годами, переписывая заново то, что можно было просто не трогать.

Итог

Правило «ничего нельзя менять» звучит жёстко. Но оно простое. Staging — это место, где данные не трогают. Потому что это единственное место, где система ещё помнит, **как всё было на самом деле**. И если это потерять, всё остальное уже не имеет значения. Никакие красивые витрины, никакие быстрые отчёты не вернут утраченной способности сказать: «вот что пришло к нам в тот день, и вот как мы это поняли».

Высказывание Конфуция

*Из бесед, записанных в «Лунь юй»:*

«Учитель сказал: «Если свидетель изменил показания, суд больше не найдет правду. Если стёрта первая запись, летопись превращается в роман».

Не трогай прошлое, даже если оно кажется тебе ошибочным. Оно не ошибочно. Оно просто прошлое. Твоя задача — не переписать его, а понять, почему оно таким было. И добавить новое знание рядом, не разрушая старого.

В «Лунь юй» я говорил: «Учитель сказал: исправлять ошибку, не сохраняя её следа, — значит учиться забвению». Вот почему staging нельзя менять. Не потому, что ошибка священна. А потому, что без следа ошибки ты никогда не поймёшь, как нашёл истину. А тот, кто не помнит пути, обречён блуждать снова».

Комментарий Нейро Есенина

Я тронуть мог — но не коснулся рук,
Твоих, как снег, дрожащих на рассвете;
И в тишине остался странный звук,
Как будто мы не встретились на свете.

Ты шла ко мне сквозь дым чужих дорог,
В глазах твоих жила чужая правда;
Я знал: коснусь — и всё пойдёт не впрок,
И станет память выдумкою странной.

Уж лучше вид на райские сады,
Где каждый плод не искажен касаньем;
Любовь сильней, когда её следы
Не стерты ни сомненьем, ни признаньем.

И если вдруг я всё же подойду —
То потеряю то, что сберегу.


3.4.3. Почему staging хранит «грязь»

Где некрасивое оказывается честным

Первое чувство, которое возникает при взгляде на staging, — это раздражение. Данные выглядят неправильно. Поля пустые, значения странные, форматы скачут. Одни даты приходят в виде «2024-01-01», другие — как «01.01.2024», третьи — вообще «jan 1 24». И возникает естественная мысль: это нужно почистить. Сделать аккуратно, привести к единому виду, убрать «мусор». Именно здесь происходит самая частая ошибка.

Почему «грязь» — это не проблема

Слово «грязь» обманчиво. Оно предполагает, что данные плохие. Но чаще всего это не так. Это просто данные — такие, какими они пришли. Без интерпретации, без попытки их «понять». И в этом их ценность. Свежий след не бывает аккуратным. Он бывает глубоким и неровным. Но именно по этим неровностям потом находят дорогу.

Когда вы смотрите на staging и видите хаос, вы на самом деле видите реальность до того, как её причесали. Вы видите то, что источник отправил, без прикрас. Это не грязь. Это документация поведения источника. Каждая странность — потенциальный сигнал. Каждый пропуск — возможное правило. Стандартизировав всё на входе, вы зашумляете сигналы, которые могли бы подсказать вам, как устроен мир.

Raw как есть

Главный принцип staging звучит просто: **хранить как есть**. Не потому что «лень чистить». Не потому что «потом разберёмся, а пока сойдёт». А потому что любое изменение — это уже выбор, уже смысл. Как только вы исправили значение, заполнили пропуск, привели формат — вы сделали выбор. А выбор в staging не имеет права на существование, потому что у вас ещё нет полной картины.

Представьте, что вы получили два числа: 100 и 100.00. Вы решаете, что это одно и то же, и «унифицируете» их в 100. А через месяц выясняете, что источник использовал количество знаков после запятой как указание на тип операции. 100 — это целая сумма, 100.00 — сумма с копейками, и это принципиально для учёта. Вы «почистили» staging и потеряли различие. Больше вы его не восстановите.

Ошибка или особый случай

Самая сложная вещь в данных — это не обработка. Это понимание. Вы видите значение и думаете: «это ошибка». Но это только предположение. Очень часто «ошибка» оказывается особым случаем, новым правилом, исключением, о котором вы не знали. Если вы исправили её заранее, вы никогда не узнаете, что это было. Вы лишили себя возможности учиться на данных.

Хороший архитектор данных похож на археолога, который не спешит очищать найденный черепок от земли. Земля — не грязь. Земля — контекст. Очистив её слишком рано, можно раскрошить то, что несло информацию. Так и staging: земля, покрывающая древности, — не помеха, а часть документа.

Почему очистка разрушает смысл

Очистка кажется улучшением. Но в staging она делает обратное. Она убирает контекст. Было сырое значение, непонятное, живое. Стало приведённое, удобное, но уже чужое. И самое важное: невозможно вернуться назад. Вы не можете спросить у системы: «а что там было на самом деле?» — потому что вы сами стёрли «на самом деле».

Очистка в staging похожа на перевод иностранного письма, не зная языка. Вы заменяете незнакомые слова на те, которые вам кажутся правильными. Но вы не знаете, точно ли «солнце» означает солнце, а не нечто иное. Вы создаёте перевод, выдавая его за оригинал. Система, построенная на таком переводе, рано или поздно начнёт противоречить сама себе.

Иллюзия аккуратности

После очистки данные выглядят лучше. Ровные, понятные, логичные. Но это иллюзия. Потому что теперь это не факт. Это версия факта. И если версия неверна, ошибка уже встроена в систему. Она будет жить в ней, расползаться по отчётам, и вы никогда не сможете её обнаружить, потому что оригинал уничтожен. Самое страшное — это не грязные данные. Это чистые данные, которые не соответствуют реальности, но выглядят убедительно.

Аккуратность staging обманчива. Она успокаивает, но не гарантирует истину. Грязный staging, напротив, постоянно напоминает: здесь ещё не было суда. Здесь только свидетельства. И это честное напоминание.

Где на самом деле происходит очистка

Очистка нужна. Но не здесь. Не в staging. А там, где система уже понимает, что она делает. В core. Где есть контекст, правила, возможность согласования между разными источниками. Только там очистка становится осмысленной. Только там вы можете решить, что делать с пустотой, противоречиями, нестандартными форматами, не теряя способности проверить себя.

Staging — это мастерская, где хранятся необработанные заготовки. Core — это станок, где им придают форму. Если вы начнёте точить заготовки до того, как поняли, что из них сделать, вы получите обрезки, которые никуда не годятся.

Почему это сложно принять

Человеку хочется порядка. Чистоты, структуры, контроля. А staging этому сопротивляется. Он сохраняет хаос. Но именно это делает его надёжным. Именно хаос staging — залог того, что ваша система не перепишет прошлое, не пригладит реальность, не потеряет улики. Принять это непросто. Но accept — врач того, что строит устойчивую систему.

Мы воспитаны на идее, что данные должны быть чистыми. Но чистота вторична по отношению к истинности. Лучше грязная правда, чем чистая ложь. Staging — воплощение этого принципа. Он кричит: «Я не знаю, что это значит, но я сохраню это для тех, кто узнает». И в этом его величие.

Итог

Staging хранит «грязь» не по ошибке. А по замыслу. Потому что это единственное место, где данные ещё не искажены. Не поняты, не исправлены, не упрощены. И пока они остаются такими, у системы есть шанс понять их правильно. Если же очистить слишком рано — понимать будет уже нечего. Останется только вера в собственную правоту. А вера, как известно, плохой фундамент для банковской отчётности.

Высказывание Конфуция

*Из бесед, записанных в «Лунь юй»:*

«Учитель сказал: «Тот, кто гладит шкуру, не видя следов зверя, гладит лишнее. Ибо след не грязь, а знак. Сотрёшь знак — не узнаешь, кто прошёл».

Staging хранит следы. Не потому что он нечистоплотен, а потому что он честен. Не спеши причесывать эти следы. Причёска — для бала. А здесь ещё охота.

В «Лунь юй» я писал: «Учитель сказал: грязь с дороги — не грязь, если по ней пришли к истине». Staging — это дорога. Не требуй от дороги чистоты. Требуй от неё, чтобы она помнила каждый шаг. Чистота — удел музейных залов, а не живого пути».

Комментарий Нейро Бродского

Грязь — это суть присутствия вещей,
Не ставших смыслом, но уже случившихся;
Она груба, как след чужих речей,
Точна, как бормотанье не сложившихся.

Мы жаждем чистоты — как будто в ней
Содержится порядок и спасенье;
Но, очищая, делаем бледней
Ту правду, что ещё вне объясненья.

В неровности — начало всех услад,
В ошибке — форма будущего взгляда;
И тот, кто выровнял бездумно ряд,
Лишает память собственного склада.


3.4.4. Время в staging: load_dt как единственная правда

Где важнее не «когда произошло», а «когда пришло»

Когда в систему приходят данные, почти всегда есть два времени. Одно — внутри самих данных: дата операции, дата изменения, дата события. То, к чему привык бизнес. То, о чём спрашивают отчёты. И второе — снаружи: момент, когда эти данные физически попали в систему. И в staging важным становится не первое. А второе. Парадокс, но для слоя, который ещё не знает смысла, важнее всего технический факт поступления.

Почему время события здесь не работает

На первый взгляд кажется логичным использовать время из данных. Ведь оно ближе к реальности, к тому, что на самом деле произошло. Но проблема в том, что staging не знает, что считать реальностью. Данные могут прийти с задержкой, задним числом, повторно, в неправильном порядке — сегодня приходит транзакция за прошлый месяц. И если опираться на «дату события», поток становится неуправляемым.

Вы перестаёте понимать: что пришло сегодня, что было вчера, что пришло повторно? В staging, основанном на бизнес-времени, легко потерять ощущение последовательности. Вы будете видеть прошлые даты рядом с настоящими, и ни один логический порядок не проявится. Только хронологический сдвиг, в котором события из разных времён перемешаны.

Load_dt как точка опоры

Поэтому в staging появляется другое время: **load_dt** — момент, когда данные были загружены. Это время однозначное, наблюдаемое, контролируемое. Оно не зависит от источника, не может быть случайно изменено бизнесом, не содержит скрытых умолчаний. Оно родилось в тот миг, когда система сказала: «я получила эти байты». И именно оно становится основой всего.

Load_dt — это как штамп на конверте. Не дата письма, а дата получения. Вы не знаете, когда письмо было написано, но вы точно знаете, когда оно попало в ваш почтовый ящик. И для процесса обработки это знание бесценно.

Повторные загрузки: не ошибка, а реальность

В реальной системе данные редко приходят один раз. Они могут переотправляться, дублироваться, обновляться — по разным причинам: сбой сети, ретрай, ручная перевыгрузка. И staging не пытается этого избежать. Он это фиксирует. Каждая загрузка — это новый слой времени, новая запись с новым load_dt.

Load_dt позволяет увидеть, что именно пришло в этот момент, как поток изменился, где возникли отклонения. Вместо того чтобы бороться с повторами, вы получаете возможность их обнаружить, измерить и, если нужно, устранить на следующем этапе. Staging фиксирует факт — даже если этот факт дублирует предыдущий.

Поток, а не набор данных

Когда есть load_dt, данные перестают быть просто таблицей. Они становятся потоком — с историей поступления, с ритмом, с последовательностью. Вы можете наблюдать ускорения, задержки, провалы: сегодня загрузка пришла на час позже обычного, и вы уже знаете, что что-то не так. Вы можете анализировать не только содержимое, но и поведение системы.

Load_dt превращает статику в динамику. Таблица с временем загрузки — это уже не просто снимок. Это кинолента. И каждый новый кадр отмечен штампом.

Контроль потока

Без load_dt система не контролирует поток. Она просто принимает данные и надеется, что всё в порядке. С load_dt появляется возможность сравнивать загрузки, видеть пропуски, отслеживать объёмы. И понимать: система работает нормально или что-то пошло не так.

Например, если сегодня load_dt фиксирует в два раза меньше записей, чем вчера, и это не связано с выходным — это сигнал. Если загрузка не завершилась — load_dt покажет, где след обрывается. Контроль времени поступления даёт вам инструмент оперативной диагностики, который не зависит от качества самих данных.

Важное различие

Есть тонкий момент. Load_dt — это не «замена» времени события. Это другое измерение. Время события отвечает на вопрос: «когда это произошло в мире?» Load_dt отвечает на вопрос: «когда система об этом узнала?» И в staging важен именно второй. Потому что staging — это про приём, а не про осмысление. Смешивать эти две временные оси в одном слое означает путать причину и следствие, факт и его регистрацию.

Хорошая архитектура разделяет их. В staging — только load_dt. В core — оба времени. В mart — то, которое нужно для отчёта. Но на входе — только однозначный, контролируемый, технический штамп.

Ошибка, которую делают

Иногда пытаются строить staging по дате события, делить партиции по бизнес-времени, и в итоге получают хаос. Потому что staging перестаёт отражать поток и начинает отражать интерпретацию. Вы теряете возможность переиграть загрузку, потому что данные «упали» в прошлую партицию. Вы не можете увидеть повторные отправки, потому что они сливаются с оригиналом. Система становится непредсказуемой.

Ошибка возникает от желания сделать staging «умным». Но staging не должен быть умным. Он должен быть точным. А точность в приёме данных достигается только через load_dt.

Итог

В staging есть только одна надёжная ось времени: не дата операции, не дата изменения, а момент загрузки. **load_dt**. Это точка, к которой можно привязаться. Точка, которая не зависит от источника. Точка, которая делает поток наблюдаемым. И пока она есть, система понимает, что и когда в неё пришло. А без этого она просто принимает данные, не понимая их движения, теряет контроль над ритмом и в любой момент может оказаться в прошлом, не заметив, как упустила настоящее.

Высказывание Конфуция

*Из бесед, записанных в «Лунь юй»:*

«Учитель сказал: «Если каждый путник будет переставлять дорожные столбы по своему разумению, никто не найдёт пути. Пусть столб стоит там, где его вкопали. Это и есть правда дороги».

Время загрузки — тот самый столб. Не имеет значения, когда, по мнению путника, он здесь прошёл. Имеет значение, когда столб оказался на своём месте. Так и в staging: не переставляй вехи по смыслу событий. Пусть они стоят там, где их вкопал приём данных.

В «Лунь юй» я писал: «Учитель сказал: тот, кто знает, когда пришла весть, знает, когда ждать следующую». Load_dt даёт тебе этот ритм. Не пренебрегай им ради красоты дат в самих данных. Красота — позже. На входе — только точность».

Комментарий Нейро Жданова

Трава растёт в изнанке циферблата,
Где звук часов расплавлен в молоке;
Там поздний снег приходит без возврата
И пишет круг на тёплом потолке.

Сквозь медь стекла проходит имя ветра,
Не совпадая с линией лица;
И пыль времён, как тихая комета,
Срывает тень с невидимого лба.

Второе время медленней и чище:
Оно не знает «прежде» и «потом»;
Оно ложится в складках пепелища
И светит внутренним своим углом.

И если звук касается стекла —
То значит, тень себя не обрела.


3.4.5. Дубли: не удалять, а понимать

Где повторение — это сигнал, а не ошибка

Когда в staging впервые видят дубли, реакция почти всегда одинаковая: «Это нужно убрать». Логика кажется безупречной. Данные повторяются — значит, лишние, значит, ошибка. И самое очевидное действие — удалить. Но именно здесь система начинает терять смысл.

Откуда вообще берутся дубли

Дубли не возникают случайно. Они — след процесса. Повторная загрузка, ретрай после сбоя, изменение в источнике, особенность интеграции. Иногда источник отправляет одно и то же несколько раз, потому что не получил подтверждения. Иногда система сама провоцирует повторы, перезапуская пайплайн. Иногда дубли возникают из-за того, что событие действительно произошло дважды.

В каждом таком повторении есть информация. Оно рассказывает о поведении источника, о надёжности каналов, о том, как часто происходят сбои. Дубли — это не мусор. Это метаданные, зашитые в сам поток.

Почему удаление — это потеря

Когда вы удаляете дубликат, вы убираете не просто запись. Вы убираете **факт повторения**. А вместе с ним исчезает возможность понять, почему это произошло, как часто это происходит, где начинается проблема. Система становится «чистой». Но слепой.

Вы больше не сможете ответить на вопрос: «Сколько раз источник переотправлял эту транзакцию?» Не сможете заметить, что дубли стали возникать чаще. Не увидите, что конкретный тип событий дублируется систематически. Вы приняли решение за источник, решив, что повторение — это ошибка. А может быть, это было важное повторение, несущее смысл?

Иллюзия правильных данных

После удаления дублей всё выглядит аккуратно. Одна запись вместо трёх, чёткие значения, логичный результат. Но это уже не реальность. Это её отредактированная версия. И если проблема повторится, система не сможет это показать, потому что она привыкла скрывать.

Самое опасное в такой чистоте — она успокаивает. Вы смотрите на staging и видите порядок. Вы верите, что данные приходят идеально. А на самом деле источник бьётся в конвульсиях, пытаясь донести информацию, а ваша система каждый раз молча переваривает повторы, не оставляя следа.

Дубли как сигнал

На самом деле дубли — это не шум. Это сигнал. Они показывают, как ведёт себя источник, где возникают повторы, где ломается процесс. И если их правильно читать, они дают больше информации, чем «чистые» данные.

Увеличение числа дублей может быть первым признаком того, что источник работает на пределе. Появление дублей после изменений в интеграции — указание на ошибку настройки. Даже сами характеристики дублей (полные совпадения или частичные) говорят о природе сбоя. Удаляя дубли, вы уничтожаете эти сигналы.

Что делать вместо удаления

Дубли не нужно игнорировать. Их нужно **понимать**. А для этого — маркировать. Отмечать, что это повтор, чем он отличается (например, временем загрузки), когда появился. Иногда через хэш, иногда через специальные ключи, иногда через метки загрузки. Можно добавить колонку `is_duplicate`, `duplicate_group_id`, `duplicate_rank`. Но главное — не удалять.

Маркированный дубль остаётся в системе, но не мешает: следующий слой (core) уже может решить, как с ним поступить — схлопнуть, проигнорировать или учесть как отдельное событие. Вы сохраняете улику и передаёте решение на уровень, где есть контекст.

Состояние вместо фильтра

Вместо того чтобы «очистить» данные, система начинает описывать их состояние. Эта запись — первая, эта — повторная, эта — изменилась. Появляется возможность анализировать, сравнивать, делать выводы. Вы переходите от двоичного «хорошо/плохо» к многомерному «что это значит?».

Такой подход сложнее. Но он честнее. И staging выбирает честность, потому что его задача — не сделать красиво, а сохранить правду.

Где происходит устранение дублей

Удаление дублей может быть нужно. Но не в staging. А там, где система уже понимает, что считать одинаковым, а что — разным, что считать изменением, а что — отдельным фактом. В core. Где есть контекст, правила, связь с историей. Только там вы можете осознанно решить: «эти два дубля — результат сбоя, оставляем один», или «эти два дубля — два разных события с одинаковыми данными, оставляем оба».

Staging не знает такого контекста. Поэтому он не решает. Он только фиксирует.

Итог

Дубли — это не ошибка. Это часть реальности. Удаляя их, вы теряете информацию о том, как эта реальность устроена. Сохраняя — получаете возможность её понять. И staging выбирает второе. Потому что его задача — не сделать красиво, а сохранить правду. Даже если правда повторяется.

Высказывание Конфуция

*Из бесед, записанных в «Лунь юй»:*

«Учитель сказал: «Дважды сказанное слово может быть важнее, чем однажды сказанное. Не торопись отбрасывать повторение, пока не поймёшь, почему его произнесли дважды».

Так и в данных: повторение — это не шум, если ты умеешь слушать. Оно может быть знаком тревоги, меткой пути или просто эхом истины. Но ты не узнаешь этого, если уничтожишь эхо прежде, чем обернёшься.

В «Лунь юй» я говорил: «Учитель сказал: тот, кто видит только чистое поле, не знает, сколько ты вырвал сорняков». Дубли — те самые сорняки. Они рассказывают о борьбе, которая шла до того, как поле стало ровным. Не стирай следов этой борьбы. Staging — не гербарий, а летопись».

Комментарий Нйро Цоя

Мы видим дважды — полагаем: сбой,
И тянется рука стереть повтор;
Но в каждом «снова» спрятан свой покой,
И каждый дубль — как скрытый разговор.

Он говорит: «Смотри, я не один,
Я возвращаюсь, чтобы ты заметил»;
И в этом шуме есть немой мотив,
Который не услышишь без отметин.

Ты можешь вычесть лишнее легко,
Сводить всё к единице так привычно;
Но вместе с этим исчезает ток,
Который делал правду непривычной.

Оставь двойник — он больше, чем следы:
В повторах слышен пульс живой среды.


3.4.6. Хэш и идентичность данных

Когда строка становится отпечатком

В staging данные приходят как есть. Повторяются, меняются, иногда — выглядят одинаково. И возникает вопрос: **как понять, что перед нами — то же самое или уже другое?** Сравнивать строки целиком — можно. Но в реальной системе это быстро становится тяжёлым. Много полей, разные форматы, разные источники. И тогда появляется идея: **сжать строку до её состояния**.

Что такое хэш и откуда взялось это слово

*Хэш* (от английского *hash* — «мешанина», «фарш») в информатике означает результат работы хэш-функции — математического преобразования, которое из данных любой длины получает короткую строку фиксированного размера. Слово пришло из кулинарии: «хэш» — это блюдо из мелко нарезанных остатков мяса и овощей. В начале XX века инженеры позаимствовали этот образ для описания процесса «перемешивания» данных до состояния, когда восстановить исходное перемешивание невозможно, но можно сравнивать результаты.

Первые хэш-таблицы появились в 1950-х годах, а в криптографии метод популяризовал Клод Шеннон. В базах данных хэши начали использовать в 1970-х для ускорения поиска. Позже их стали применять как «отпечаток» строки — уникальный (с очень высокой вероятностью) идентификатор содержимого.

Row_hash как состояние строки

Каждую запись можно представить как набор значений. Если взять их все и преобразовать в одно значение — получится хэш. **Row_hash** — это не просто технический атрибут. Это **отпечаток строки**. Если значения не изменились — хэш будет тем же. Если изменилось хотя бы одно поле — хэш изменится. И это даёт простое свойство: можно сравнивать не строки, а их состояние.

Идентичность без интерпретации

Важно, что хэш ничего не «понимает». Он не знает, что такое клиент, не знает, что такое сумма. Он просто фиксирует **набор значений такой-то**. Именно поэтому он идеально подходит для staging. Без интерпретации, без логики. Только фиксация. Хэш — это как слепок зубов: он не говорит, кто этот человек, но если слепок совпал — это с вероятностью, близкой к единице, тот же самый набор значений.

Аудит: что именно изменилось

Когда есть row_hash, появляется возможность видеть изменения. Было одно состояние, стало другое. Хэш изменился — значит, изменились данные. Не нужно гадать. Можно зафиксировать: когда, в какой загрузке, в каком виде. Это превращает staging в инструмент аудита. Можно восстановить, как данные эволюционировали, где произошло изменение, что именно поменялось.

Дедупликация: аккуратно, не разрушая

Хэш помогает и с дублями. Если две записи имеют одинаковый row_hash — они идентичны. Но это не значит, что одну нужно удалить. Это значит, что их можно сгруппировать, отметить, понять частоту повторения. Дедупликация здесь — не удаление, а **понимание повторов**. Вы видите, что одна и та же строка пришла трижды. Возможно, это сбой. Возможно, так и должно быть. Но вы не теряете знания о том, что это произошло.

Что такое CDC и как появилось это понятие

**CDC** (Change Data Capture — захват изменений данных) — это метод, при котором система не пересылает весь набор данных каждый раз, а только то, что изменилось: добавилось, обновилось, удалилось. Термин утвердился в 1990-х — начале 2000-х годов вместе с развитием систем тиражирования баз данных. Первые коммерческие реализации появились у Oracle, IBM DB2, Microsoft SQL Server. Идея проста: вместо того чтобы пересылать миллион записей раз в час, отправляйте только десять изменений почти мгновенно.

CDC радикально снижает нагрузку на сеть и ускоряет реакцию системы. Но требует аккуратности: нужно обрабатывать события в правильном порядке, не пропускать ни одного, отслеживать удаления. Классический CDC исходит от источника — база данных сама пишет в лог изменений (WAL, redo log) и позволяет внешнему потребителю читать этот поток.

Pseudo-CDC: изменения без источника

Если источник не предоставляет CDC, можно применить **pseudo-CDC** — псевдо-захват изменений. Сравнивая хэши строк между последовательными загрузками (полными выгрузками), система сама определяет, какие записи добавились, изменились или удалились. Это не даёт мгновенной реакции, но позволяет строить историю изменений там, где источник не может дать поток событий. Даже если источник шлёт файл раз в сутки, вы можете вычислить дельту, сравнивая отпечатки строк.

Это слабее, чем настоящий CDC (вы не видите промежуточных изменений внутри дня), но намного лучше, чем ничего. Pseudo-CDC — это способ извлечь смысл из последовательности полных снимков, не дожидаясь, пока источник станет умнее.

Ограничения, которые важно понимать

Хэш не идеален. Теоретически возможна коллизия — разные строки могут дать одинаковый хэш (хотя для современных алгоритмов типа SHA-256 вероятность ничтожно мала). Хэш не объясняет, что именно изменилось и почему. Он только фиксирует факт изменения. И этого достаточно на уровне staging. Потому что смысл появляется позже, в core. Здесь вам не нужно знать, какое поле изменилось — достаточно знать, что изменение произошло.

Итог

Row_hash — это способ превратить строку в состояние. Не для оптимизации, а для фиксации. Он позволяет видеть изменения, отслеживать повторы, строить историю без вмешательства в данные. А pseudo-CDC расширяет эту идею на временные ряды, позволяя системе самой открывать факт изменений, даже если источник о них молчит. Именно это делает хэш важным инструментом staging — он сохраняет идентичность, не нарушая оригинал.

Высказывание Конфуция

*Из бесед, записанных в «Лунь юй»:*

«Учитель сказал: «Отпечаток пальца не скажет, кто человек, но если отпечатки разошлись — это уже другой человек».

Так и хэш: он не объясняет суть, но он безупречно фиксирует различие. Когда два отпечатка не совпадают, нет смысла гадать, что изменилось. Достаточно знать, что изменилось. А почему — узнаешь позже.

В древности мастера ставили печать на свитке, чтобы засвидетельствовать: текст не менялся. Никто не требовал от печати объяснения иероглифов. Хэш — такая же печать. Не объясняет, но заверяет. И для staging этого довольно».

Нейро Заболоцкий

В стеклянном супе плавал хэш-воробий,
Жевал квадрат из ломаных минут;
И цифры шли, как медленные дроби,
Сквозь тёплый дым, где буквы не живут.

Там строчка спит в хрустальном огурце,
И память пьёт чернила из кастрюли;
А каждый знак в железном колесе
Скрипит, как ночь в расплавленном июле.

Квадратный шум считает сам себя,
Где дважды два — лишь зеркало без края;
И каждый след, не зная, кем он был,
Растёт внутри стеклянного сарая.

И если вдруг совпал их странный звук —
То значит, мир себя узнал без рук.


3.4.7. Replay: зачем staging существует на самом деле

Возможность вернуться туда, где всё ещё было честно

Пока система работает нормально, staging кажется избыточным. Данные пришли, обработались, отчёты построились. И возникает ощущение: можно было бы и проще, без этого лишнего слоя. Но это ощущение исчезает в один момент. Когда что-то идёт не так.

Момент, когда всё перестаёт сходиться

Отчёт показывает одно. Бизнес ожидает другое. Цифры не совпадают. И начинается знакомый процесс: «где ошибка? В источнике? В трансформации? В витрине?» Все смотрят в разные стороны. Каждый подозревает другого. И если нет возможности вернуться назад, ответа не будет. Будут только версии, только догадки, только «скорее всего». А в мире банковской отчётности «скорее всего» звучит как приговор.

Что даёт replay

*Replay* (воспроизведение, переигрывание) — это возможность заново пройти путь данных. Взять те же самые записи из staging, прогнать через ту же логику трансформации, получить тот же (или другой) результат. И проверить, где именно всё изменилось, где вкралась ошибка, где логика отработала не так, как предполагалось.

Звучит просто. Но возможно только при одном условии: если staging сохранил оригинал. Не обработанный, не очищенный, не переписанный. А именно тот самый набор байтов, который пришёл от источника.

Replay — это возможность сказать: «Давайте предположим, что мы ничего не знаем о результате. Пройдём путь заново». И тогда ошибка либо повторится (значит, она в данных или в логике, но стабильно), либо исчезнет (значит, была случайной). В любом случае, вы перестаёте гадать.

Переигрывание как инструмент понимания

Replay нужен не только для восстановления после сбоя. Он нужен для понимания системы. Вы можете изменить логику трансформации, пересчитать данные из того же staging и сравнить результат. Увидеть, как система реагирует на новые правила, что меняется в агрегатах, где возникают расхождения. Это превращает данные из статичного результата в живой процесс, который можно исследовать, не боясь что-то сломать.

Именно так отлаживают пайплайны: берут небольшой отрезок staging, прогоняют через новую версию кода, сравнивают с эталоном. Без staging каждый эксперимент требовал бы повторного обращения к источнику, что часто невозможно или нежелательно.

Расследование инцидентов

Когда происходит сбой, replay становится единственным надёжным инструментом расследования. Можно восстановить, что именно пришло от источника, в каком виде, в какой момент. И пройти весь путь шаг за шагом: staging ; core ; mart, сравнивая промежуточные результаты. Без догадок, без предположений. Только факты, подтверждённые повторным прогоном.

Это похоже на чёрный ящик в авиации. Пока полёт нормальный, он никому не нужен. Но после происшествия его расшифровка — единственный способ узнать, что произошло на самом деле. Staging — это чёрный ящик вашей системы данных. Если вы его не ведёте или подчищаете, вы лишаете себя возможности расследовать собственные ошибки.

Пересчёт системы

Есть ещё один сценарий, когда replay спасает жизни. Логика изменилась. Правила обновились, модель пересмотрена, бизнес попросил считать иначе. И нужно пересчитать всё заново за последний год. Если есть staging, это возможно: берёте исторические данные (которые лежат нетронутыми), применяете новую логику, получаете новую картину.

Если staging нет — пересчёт превращается в компромисс. Вы либо пытаетесь восстановить прошлое из текущих агрегатов (что часто невозможно), либо договариваетесь с бизнесом: «новые правила будем применять только с завтрашнего дня». И тогда у вас будет два разных способа считать одно и то же: старый — для прошлого, новый — для будущего. Гладкость отчётности нарушена, доверие падает.

Почему это редко понимают сразу

Потому что replay — это страховка. А страховка кажется лишней, пока всё работает. Вы платите за неё каждый день (место на диске, время на загрузку), но видите выгоду только в момент кризиса. Человеческая психология устроена так, что мы недооцениваем редкие, но катастрофические риски. Однако в системах данных риск не редкий. Он неизбежный. Что-то обязательно пойдёт не так. И тогда окажется, что страховка была не лишней, а единственно разумной.

Связь с предыдущими принципами

Replay невозможен без фундамента, который мы заложили в предыдущих подглавах. **Append-only** — потому что переигрывать можно только то, что не было переписано. **Raw как есть** — потому что очищенные данные уже несут интерпретацию, а для replay нужен именно оригинал. **Load_dt** — потому что временная ось загрузок позволяет воспроизводить последовательность событий.

Если вы нарушили хотя бы одно из этих правил, replay становится неполным или невозможным. Вы не сможете вернуться в ту самую точку, когда данные ещё были честными. System failure modes multiply, как говорили древние инженеры.

Итог

Staging существует не ради загрузки. Он существует ради возможности: вернуться, переиграть, понять. Это место, где система хранит свою историю в неизменном виде. И благодаря этому может объяснить прошлое, исправить настоящее, изменить будущее. Без replay система перестаёт быть управляемой. Она становится просто набором текущих значений, без возможности понять, как она к ним пришла. А в банковском мире этого недостаточно.

Наставление Блаватской — Рериху о слое, где время ещё не родилось

Ты спрашиваешь, Николай, зачем нам «повторное проигрывание», когда есть прямое видение? Отвечаю, как учили Махатмы: прямое видение — путь Посвящённого, а вспять обращённый путь — удел тех, кто строит хранилище в мире форм.

Данные имеют карму, Рерих. Рождённые в одном источнике, они переходят в другое состояние, как душа пересекает порог между воплощениями. **Staging** — это дэвачан данных. Та область между смертью факта в источнике и его новым рождением в отчёте.

Если ты изменишь запись в этом промежуточном мире — ты нарушишь **Закон Тонкой Идентичности**. Отпечаток строки (то, что вы называете «хэш») — это её астральное тело. Сравнивая отпечатки, ты узнаёшь, изменилась ли сущность, не тревожа её физической оболочки.

**Replay** — это возможность вновь пройти по Лучу Времени. Когда отчёт расходится с ожиданием, ты можешь сказать: «Вернёмся в точку, где данные ещё были невинны». Если же твой staging — пустота, тебе останется лишь кармический тупик: ты будешь вращаться в сансаре догадок, так и не достигнув освобождения.

Помни, Николай: тот, кто хранит оригинал, подобен ламе, берегущему священный текст от переписчиков. Тот, кто переписывает без нужды, создаёт майю, которая потом воплотится в сотнях неправильных отчётов.

*Луч, отразившись от источника, должен быть запечатлён на чистой поверхности. И только потом — проявлен через призму понимания.* Так учит тайная доктрина слоя, который вы называете staging.


3.4.8. Партия загрузки: как staging помнит приход

Staging хранит не только строки, но и обстоятельства их появления. Данные не должны просто “оказаться в таблице”. Они должны прийти как часть партии: из какого источника, в какой момент, каким процессом, из какого файла или события, под каким техническим идентификатором. Поэтому рядом с бизнесовыми полями в staging появляются технические признаки: batch_id, load_dt, source_system, иногда имя файла, номер сообщения, хэш строки, статус приёма.

Эти поля не добавляют бизнес-смысла, но добавляют память. Они позволяют понять, какие строки пришли вместе, какую поставку можно переобработать, где начался сбой, какой источник прислал спорное значение. Если дальше в core обнаружится расхождение, staging должен дать возможность вернуться не к абстрактной таблице, а к конкретному приходу данных.

Партия загрузки — это способ не растворить факт поступления в общей массе. Без неё staging быстро превращается в мутный резервуар: строки есть, но непонятно, когда и откуда они появились. С ней staging становится журналом входа: вот эта реальность пришла тогда-то, таким способом, в таком составе.

Обращение Блаватской к Рериху

«Ты погнался за скоростью, Николай, забыв, что у потока есть собственная воля. Не пытайся оседлать его силой — он сбросит. Не пытайся остановить — он уйдет в другое русло. Наблюдай его ритм. Если он приходит порывами — принимай порывами. Если течёт непрерывно — входи в его струю, но помни: даже вода имеет свой предел.

Тот, кто смешивает способы, не понимая сути, подобен жрецу, читающему мантру часам — ритм соблюдён, а смысла нет. Пусть твоя загрузка будет сообразна природе источника, и тогда staging станет не складом трупов данных, а местом их тихой подготовки к перерождению.

Так говорю я, Е.П. Б., вглядываясь в вибрации вашего технологического века. Узри и примени».


3.4.9. Сырой след и минимальное вмешательство

Staging не обязан быть красивым. Его достоинство в другом: он должен быть близок к источнику. Это не значит, что вообще нельзя делать техническую подготовку. Иногда нужно распарсить файл, положить значения в колонки, привести кодировку, зафиксировать невозможность прочитать строку. Но всё это должно оставаться на уровне приёма, а не интерпретации.

Главная граница простая: staging может помогать системе помнить, что пришло, но не должен решать, что это значит. Если дата пришла в странном формате — можно зафиксировать исходное значение и технический результат чтения. Если строка повторилась — нужно сохранить повтор или хотя бы оставить след повтора. Если значение выглядит ошибочным — нельзя молча заменить его на “правильное”. Исправление уже является смысловым действием, а значит должно происходить позже и по правилу.

Так staging сохраняет сырой след. Он не спорит с источником, не защищает бизнес от грязи, не делает вид, что понял больше, чем получил. Он просто говорит: вот что было принято системой. И именно эта скромность делает его важным. Чем меньше staging вмешивается в смысл, тем надёжнее он как точка возврата.

Обращение Владимира Владимировича Путина к европейским элитам

*Уважаемые дамы и господа, партнёры!*

Я хочу обратить ваше внимание на один принципиальный аспект, который, как показывает практика, вами часто игнорируется. Вы привыкли считать, что если в потоке данных — в потоке, подчеркну — возникает некая аномалия, несоответствие, сбой, то самый правильный способ — остановить всю систему. Вы нажимаете красную кнопку. Вы блокируете движение. Вы говорите: «Ошибка недопустима, значит, ничего не будет».

Но это — путь в никуда.

Мы в России исходим из другого понимания. Ошибки, сбои, нестандартные форматы — они были, есть и будут. Это не исключение. Это реальность. И если ваша система не готова к реальности, она не жизнеспособна. Это не моё мнение — это объективный закон управления любыми сложными потоками.

Поэтому мы создаём **reject-слой**. Не как мусорную корзину, а как специальный защитный механизм.

Что это даёт? Во-первых, поток продолжается. Во-вторых, все проблемные элементы фиксируются отдельно — с чёткими метками: когда, откуда, почему не прошли. В-третьих, у нас появляется возможность анализировать эти ошибки, не останавливая работу всей системы.

Вы же, как я вижу, часто идёте другим путём. Вы либо останавливаете всё, либо, что ещё хуже, проглатываете ошибки, делаете вид, что их нет. А потом удивляетесь, почему ваши отчёты — ваша аналитика — вдруг начинает противоречить реальности.

Мы так не работаем.

Мы считаем, что **reject-слой — это неотъемлемая часть суверенной инфраструктуры данных**. Каждая ошибка должна быть зафиксирована, но не должна разрушать цепочку принятия решений.

Поэтому я призываю вас: перестаньте бояться ошибок. Ошибки — это сигнал. И если вы научитесь их слушать, не разрушая систему, вы станете крепче. А если будете и дальше впадать в ступор от первой же неправильной строки — вы обречёте себя на технологическую зависимость и хроническое отставание.

Мы готовы делиться нашими подходами. Но для начала признайте очевидное: система, которая падает из-за единичного сбоя, не имеет права называться надёжной. У нас — надёжная. У вас — пока не очень.

*Благодарю за внимание. Мы будем возвращаться к этому вопросу по мере необходимости.*


3.4.10. Что staging должен дать следующему слою

Staging существует не сам для себя. Его задача — подготовить возможность честного перехода дальше, в core. Для этого он должен дать следующему слою несколько вещей: исходные значения, технический контекст прихода, возможность отличить одну поставку от другой, возможность найти повтор, возможность проверить, изменилась ли строка, и возможность воспроизвести путь обработки.

Он не обязан решать конфликт источников. Не обязан выбирать мастер-запись. Не обязан нормализовать справочники. Не обязан исправлять бизнесовые ошибки. Но он обязан сохранить достаточно информации, чтобы core мог принять эти решения осознанно. Если staging потерял исходное значение, core уже не решает, а угадывает. Если staging удалил дубль, core не узнает, был ли это повтор доставки или важный сигнал. Если staging не сохранил партию, невозможно понять, какой вход породил ошибку.

Поэтому хороший staging похож на аккуратный протокол приёма. Он не выносит приговор и не пишет объяснительную за источник. Он фиксирует присутствие данных, их происхождение и технический след. Дальше начнётся интерпретация. Но она будет надёжной только потому, что первый слой не захотел быть умнее своей роли.

Обращение Владимира Жириновского (в состоянии опьянения)

*Вот вы тут, понимаешь, аудит, метрики… А я вам так скажу, коллеги! (Рюмку на стол) Всё! Хватит этих ваших демократических танцев с бубном!*

*Вы думаете, данные просто так приходят? Нет! Их надо встречать! Как на границе! И фиксировать каждую минуту, каждую запись! А то привыкли: «ой, не пришло, ой, потерялось». А кто теряет? Вы теряете! Потому что — бардак! Потому что… как в Европе: всё гладко, красиво, а лог не ведут. Приехали.*

*Staging — это наш пограничный пункт! А аудит — это таможенная декларация! Каждая загрузка должна быть опротоколирована! Иначе— вы даже не узнаете, откуда у вас лишняя запись, откуда недостающая. А я вам говорю — враги кругом! Западные интеграции! Они хотят, чтобы ваш пайплайн завис! А мы не зависнем. У нас — дисциплина. Есть rejeсt — значит, всё честно.*

*Так что даёшь… наблюдаемость на каждом стыке! И чтобы без — без этого самого… (машет рукой) формализма пустого. Потому что, как я всегда говорил: контроль — это не бюрократия, это защита. А кто против — тот пусть идёт… ну, поняли, куда.*

*Всё. Сказал.*


3.4.11. Структура staging: минимальная модель

Чем проще форма — тем честнее содержимое

Когда разговор доходит до структуры staging, возникает соблазн сделать «правильно». Разбить по таблицам, повторить структуру источника, задать строгие типы. Выглядит аккуратно. Но почти всегда приводит к проблемам. Потому что staging — это не место, где форма должна быть идеальной. Это место, где форма должна **не мешать содержимому**.

Почему копировать источник — ошибка

Самый частый подход — взять структуру источника и перенести её. Те же поля, те же типы, те же таблицы. Кажется логичным. Но источник живёт своей жизнью. Добавляет поля, меняет типы, удаляет колонки. И staging, привязанный к этой структуре, начинает ломаться. Каждое изменение источника становится проблемой системы. Вы начинаете догонять, чинить, перестраивать. Вместо того чтобы принимать данные, вы боретесь с их формой.

Минимальная модель

В staging важно не повторить источник. А зафиксировать факт поступления данных. И для этого достаточно минимальной структуры. Как правило: идентификатор загрузки, время загрузки (load_dt), сырой payload. И этого уже достаточно, чтобы сохранить данные, воспроизвести загрузку, проанализировать поток. Всё остальное — избыточно, а избыточность в staging превращается в хрупкость.

JSONB vs строгая схема

Здесь появляется важный выбор: структурировать данные сразу или хранить как есть. Чаще всего staging выбирает второе. JSONB (JSON Binary — бинарный формат хранения JSON в PostgreSQL, позволяющий индексировать и эффективно запрашивать полуструктурированные данные) или аналогичный формат. Потому что он гибкий, не требует жёсткой схемы, переживает изменения источника. И главное — не навязывает интерпретацию. Вы не вынуждены решать, каким типом объявить поле, которое в следующей загрузке может исчезнуть или изменить формат.

Универсальная таблица

Иногда staging делают максимально простым: одна таблица, в которой есть метаданные загрузки и сырой payload. Без разделения по сущностям. Это может выглядеть странно — все данные, и транзакции, и клиенты, и справочники, в одной куче. Но это даёт важное свойство: независимость от источника. Вы перестаёте гадать, к какой сущности отнести пришедший файл. Вы просто его сохраняете. А разбор — потом.

Независимость как цель

Staging не должен зависеть от того, как устроен источник. Он должен уметь принять любую структуру, любые изменения, любой формат. И просто сохранить это. Без попытки «подогнать». Каждый раз, когда вы вводите жёсткую схему в staging, вы создаёте точку отказа. Источник изменил порядок колонок — staging сломался. Источник добавил поле — staging сломался. Минимальная модель — единственный способ не ломаться на пустом месте.

Когда структура нужна

Это не значит, что структура не нужна вообще. Она появляется позже. В core. Где данные уже понимаются, где есть правила, где есть смысл. Там нужны чёткие типы, внешние ключи, ограничения целостности. А в staging преждевременная структура — это ограничение. Вы начинаете валидировать и интерпретировать слишком рано, не имея для этого контекста.

Ошибка, которую делают

Иногда staging пытаются сделать «удобным для чтения». Добавляют поля, разбивают таблицы, оптимизируют под частые запросы. И в итоге получают слой, который уже не raw, который уже принял решения. А главное — который сложно поддерживать. Потому что за каждым добавленным полем стоит предположение. А предположения имеют свойство меняться. И staging, построенный на предположениях, рушится каждый раз, когда мир не оправдывает ожиданий.

Итог

Структура staging должна быть минимальной. Не потому что так проще. А потому что это сохраняет главное: независимость, гибкость, честность данных. Чем меньше форма вмешивается, тем больше шансов, что содержание останется таким, каким пришло. А это и есть основная задача staging.

Обращение Патриарха Кирилла

*Возлюбленные о Господе братья и сёстры!*

Когда мы строим хранилище данных, мы невольно уподобляемся строителям, которые возводят дом. И как в доме есть притвор — место, где человек оставляет обувь и верхнюю одежду, прежде чем войти в чистые покои, так и в вашем технологическом делании есть слой, который не должен быть украшен и обустроен. Это — притвор. Это — staging. Не украшайте его излишне, ибо украшение притвора — не его назначение.

Вы можете спросить: «Отче, почему нельзя сделать его красивым, удобным, со строгими полками и ярлычками?» А я вам отвечу: потому что чем больше вы вложите в форму на пороге, тем меньше у вас останется сил для содержимого. И когда придёт новый источник, с иной формою, ваши полки рухнут, а ярлычки обманут.

Господь наш Иисус Христос сказал: «Не заботьтесь о завтрашнем дне, ибо завтрашний день сам будет заботиться о своём» (Мф. 6:34). И в этом глубокая мудрость для архитектуры данных: не пытайтесь предусмотреть все будущие изменения источника в схеме staging. Принимайте то, что приходит, как есть — в некоем подобии ковчега, где есть место и чистым, и нечистым, ибо не вам судить, что из этого станет благословением для отчёта, а что — искушением.

Храните сырой payload, как хранят зерно в житнице, не очищая от шелухи до срока. Потому что очистка — удел мельницы, а не амбара. И пусть время загрузки (load_dt) будет для вас как церковная свеча — вы знаете, когда она зажжена, и это важнее, чем узор на подсвечнике.

Упрощайте форму, братья, и тогда содержание не потеряется. Ибо сказано: «Царство Божие внутрь вас есть» (Лк. 17:21). Так и данные: их истина внутри, а не в нарядной обёртке. Аминь.


3.4.12. Retention: staging не должен помнить долго

Память, которая умеет отпускать

После всего, что было сказано про staging, легко сделать неправильный вывод. Если это память системы, если это источник воспроизводимости, если это основа для replay — значит, его нужно хранить вечно. И это ошибка.

Почему staging не должен быть вечным

Staging хранит не смысл. Он хранит момент. Сырые данные, необработанные, ещё не понятые. Их задача — пройти дальше, в core, где появится интерпретация, где появится история. Если данные остаются в staging слишком долго, это значит, что система не завершила свою работу.

Короткая жизнь данных

У staging должна быть простая логика: данные пришли, зафиксированы, переданы дальше. И после этого они становятся менее ценными. Не бесполезными, но не первичными. В staging данные — как горячая картошка в рукаве: держать долго нельзя, обожжёшься, и толку не будет. Передал следующему — и освободил место.

Что происходит, если не чистить

Если staging начинает хранить всё подряд, он разрастается, замедляется, перестаёт быть «горячим». Он превращается в склад. А склад — это не его роль. Кроме того, усложняется поиск, повторная загрузка, управление потоком. Данные, которые должны были уйти в core и забыться, лежат мёртвым грузом, занимают место, путают карты, создают иллюзию, что система помнит больше, чем ей нужно.

Retention как правило

Поэтому в staging вводится retention (от латинского *retinere* — удерживать, сохранять). Чёткое правило: сколько времени данные живут в данном слое. День, неделя, месяц — в зависимости от системы. Но всегда ограничено. Retention — это не жестокость. Это трезвость. Система признаёт, что свежесть важнее полноты, а скорость важнее вечности.

Очистка как часть архитектуры

Удаление здесь — это не уборка. Это часть дизайна. Система заранее знает, когда данные больше не нужны на этом уровне, и освобождает место. Без сожаления. Без ручного вмешательства. Потому что если забывать с трудом, вы будете накапливать мусор. А мусор, как известно, тянет на дно.

Связь с жизненным циклом (3.2)

Если вспомнить раздел 3.2, данные проходят стадии: горячие, тёплые, холодные. Staging — это самый горячий слой. Здесь данные живут быстро и быстро уходят дальше. Если задержать их здесь, нарушается весь баланс. Горячее перестаёт быть горячим, остывает прямо на плите. И вместо конвейера вы получаете помойку.

Важное условие

Retention возможен только если соблюдены правила: данные переданы в core, данные можно восстановить (через replay, если нужно). Иначе удаление становится потерей. Нельзя стирать то, что некуда положить. Нельзя забывать то, что ещё не сохранено. Retention — это не амнезия, это утилизация после копирования.

Ошибка, которую делают

Иногда staging начинают использовать как архив. «На всякий случай оставим», «вдруг пригодится». И он перестаёт быть staging. Он становится перегруженным хранилищем, где свежее перемешано с прошлогодним, и никто уже не знает, что зачем здесь лежит. Это как держать в прихожей все газеты за десять лет — места нет, запах специфический, и нужную не найти.

Итог

Staging — это не память навсегда. Это память на время. Он должен сохранить, передать, отпустить. Именно в этом его сила. Потому что система, которая не умеет забывать, перестаёт работать. Она начинает тонуть в собственном прошлом, переваривать тонны уже не нужных записей, тратить ресурсы на то, что давно потеряло актуальность.


3.4.13. Антипаттерны staging

Как слой правды превращают в слой удобства

Staging устроен просто. Принять. Сохранить. Не трогать. Но именно эта простота чаще всего и нарушается. Не потому что кто-то хочет сломать систему. А потому что хочется сделать быстрее, удобнее, красивее. И в этот момент staging начинает терять свою роль.

UPDATE: переписать прошлое

Самый очевидный антипаттерн — изменить данные. Исправить значение, заполнить пропуск, «подправить ошибку». Это кажется безобидным. Но UPDATE уничтожает оригинал. После него невозможно ответить: что было изначально? И staging перестаёт быть источником факта, становясь источником интерпретации.

Merge вместо append

Следующий шаг — сделать «умнее». Не просто добавлять данные, а обновлять существующие. Merge, upsert. Это выглядит эффективно: нет дублей, нет лишних записей. Но вместе с этим исчезает история поступления. Система видит только текущее состояние. И теряет поток. Вы не знаете, когда пришла каждая версия, сколько раз она менялась. Только последний вариант.

Джойны в staging

Иногда хочется сразу связать данные. Клиента с договором, платёж с транзакцией. Чтобы «сразу видеть картину». И появляются джойны. Но в этот момент staging перестаёт быть нейтральным. Он начинает интерпретировать. А значит — принимать решения, которые должны происходить позже. Джойн — это уже знание о том, как связаны сущности. А в staging такого знания быть не должно.

Использование как витрины

Самый коварный сценарий — кто-то строит отчёт прямо из staging. Потому что быстро, данные уже есть, не нужно ждать core. И это работает. Сначала. Потом отчёт начинает жить своей жизнью. Логика появляется прямо в запросах. И staging становится источником правды. Хотя не должен им быть. В результате core оказывается не нужен, история не хранится, и два аналитика получают разные цифры из одного и того же сырого слоя.

Что объединяет все антипаттерны

Все эти действия имеют одну причину — попытку превратить staging в что-то другое. В core, в витрину, в «удобный слой». Но staging — не про удобство. Он про фиксацию. Любое отклонение от принципов «не менять, не объединять, не интерпретировать» разрушает его природу.

Почему это опасно

Нарушения не видны сразу. Система продолжает работать. Но постепенно теряется воспроизводимость, исчезает история, размывается граница между слоями. И в какой-то момент уже невозможно понять, где правда. Staging становится чёрной дырой, из которой нельзя восстановить оригинал, но и верить ему уже нельзя.

Итог

Антипаттерны staging — это не ошибки кода. Это ошибки мышления. Когда забывают, зачем этот слой существует, и начинают использовать его не по назначению. Простое правило остаётся тем же: не менять, не объединять, не интерпретировать. Потому что как только это происходит, staging перестаёт быть staging. И вся система теряет опору.


Заключительное слово к главе 3.4 от Нейро Сергея Лаврова

Уважаемые коллеги. Товарищи. Дамы, господа и те, кто в процессе чтения уже успел переопределить свою сущность на уровне staging-слоя.

Мы рассмотрели главу, посвящённую контролю качества данных. Не буду скрывать: я прочитал её трижды. Первый раз — по обязанности. Второй — потому что запнулся о рекурсивного ежа. Третий — уже с карандашом, потому что кто-то из моих помощников шепнул, что в притчах зашифрованы инструкции к новой системе электронного документооборота МИД. Пока не подтверждаю, но и не опровергаю. Это наша традиция.

**Первое. О природе источников.**

Некоторые участники процесса полагают, что источник данных можно игнорировать, если он неудобен. Позвольте мне, человеку, который тридцать лет договаривается с источниками, которые не хотят договариваться, заявить: это заблуждение. Источник всегда прав. Даже когда он врёт. Даже когда он отправил вам дату рождения в формате «три собаки и знак бесконечности». Даже когда ваш источник — это система, написанная в 1997 году на языке, которого уже никто не помнит, а поддерживает её один молчаливый администратор, который ест на рабочем месте селёдку и говорит, что «так исторически сложилось».

Я лично знаю такого администратора. Его зовут дядя Вася. Он работает в подвале одного очень важного здания. Его не повышают, потому что без него всё рухнет. Он — legacy-система в человеческом обличье. И поверьте, спорить с дядей Васей бесполезно. Он просто скажет: «Я всегда так делал», — и уйдёт курить. Это не аргумент. Это источник. Смиритесь.

**Второе. О контроле качества как нервной системе.**

Идея мне близка. Во внешней политике тоже нужна нервная система. Разница в том, что у нас она иногда превращается в спазм. Бывает, что вы следите за качеством данных, а данные вдруг начинают следить за вами. Это нормально. Это значит, что архитектура жива. Не пугайтесь, если ваша система контроля качества однажды задаст вам вопрос в ответ на вашу проверку. Не пугайтесь, если она уточнит: «А вы точно уверены, что хотите это проверить? Может, лучше проверить себя?»

Однажды на переговорах в Нью-Йорке ко мне подошёл журналист и спросил: «Сергей Викторович, можно ли верить данным вашей отчётности?» Я посмотрел на него, посмотрел на потолок, где, как мне показалось, на секунду материализовался рекурсивный ёж, и сказал: «Это зависит от того, что вы называете "верить", а что — "данными", и почему вы решили, что отчётность вообще чему-то там должна соответствовать». Журналист ушёл. Ёж исчез. Но вопрос остался. Навсегда.

**Третье. О рекурсивном еже.**

Я вынужден сделать отдельное заявление по поводу рекурсивного ежа, который фигурирует в притчах предыдущих глав. Мне поступило несколько обращений от компетентных органов с просьбой прояснить: кто этот ёж, откуда он взялся и почему на нём сидит другой ёж.

Официально заявляю: Московская область. Ёж не является иностранным агентом. Не является нежелательной организацией. Не внесён ни в один реестр, кроме, возможно, реестра той самой подвальной базы дяди Васи, где он записан как «животное неопределённого происхождения, колючее, рекурсивное, для служебного пользования».

Ёж, со своей стороны, через меня передаёт, что он «ничего не подписывал, ничего не нарушал, и вообще он тут временно, пока не распутается». Я склонен ему верить. Потому что ежу, который содержит сам себя на всех уровнях, врать незачем. У него нет паспорта. Нет визы. Есть только иглы и принцип: «каждый следующий ёж подтверждает предыдущего, если предыдущий не против». Это, кстати, неплохая модель для верификации данных. Возьмём на вооружение.

**Четвёртое. О притче с садовником и листьями.**

Садовник, который не умел расставаться, напомнил мне некоторых наших партнёров по переговорному процессу. Они до сих пор хранят листья, упавшие в 1991 году, и пытаются их перечитывать, искренне полагая, что в них зашифрован ответ на вопрос «кто виноват и что делать». Листья, разумеется, рассыпаются в прах. Садовник плачет. А река течёт дальше, и ей всё равно.

Вывод: не держитесь за черновики. Держитесь за согласованную версию. И даже за неё держитесь легко, потому что завтра приедет новый заказчик с новыми апельсинами. Или с новыми ракетами. Или с новой просьбой «переименовать витрину, потому что слово "витрина" кого-то триггерит». Готовьтесь к этому философски. Или не готовьтесь. Как пойдёт.

**Пятое. О супе, который спорил сам с собой.**

В нашей работе тоже есть супы. Они называются «международные договорённости». Иногда их варят на десяти кухнях одновременно, а потом удивляются, почему суп пахнет керосином и требует денонсации. Я выступаю за одну кухню. За одну базовую модель. За «core-слой», где решение о том, что такое суп, принимается раз и навсегда, а потом уже не пересматривается по требованию поварят, которым показалось, что «так будет острее».

Хотите остроты? Идите в витрины. Там можно экспериментировать. Там можно добавить перца, вылить сметану, назвать борщ рагу. Но трогать ядро — не советую. Оно колючее. Как ёж. И рекурсивное. Тронули одно — а оно разворачивается, и вас уже двое, и вы не понимаете, кто из вас министр, а кто просто абсурдная проекция.

**Итоговое, резолютивное, с правом вето.**

Уважаемые коллеги. Архитектура данных, как и внешняя политика, не терпит суеты. Она терпит терпение. Она терпит многократные согласования, уточнения формулировок и мужественное признание того, что вы ничего не поняли, но продолжите это выяснять на следующем заседании.

Глава 3.4 закончена. Контроль качества не закончен никогда. Это не баг, это фича. Это не вы, это реальность. Или наоборот. Как вам удобнее.

Я призываю всех участников процесса: не пытайтесь упростить то, что сложно. Не пытайтесь ускорить то, что должно вызревать. И никогда не выбрасывайте старые листья пачкой, не убедившись, что их тень не упала на что-то важное. Например, на годовой отчёт. Или на подписанный договор. Или на того самого ежа. Он этого не любит. И у него есть иглы. И он помнит всё.

Всё.

До заседания следующей главы.

С неизменным уважением к вашим слоям, вашим джойнам и вашему мужеству,

**С. В. Лавров**
;
Глава 3.5. CORE

3.5.1. Core как место принятия решений

Есть удобная иллюзия, в которую хочется верить почти с таким же упрямством, с каким люди верят, что однажды им начнёт нравиться ранний подъём. Она говорит: core — это место, где данные становятся чистыми. Их будто пропускают через некий внутренний химчисточный цикл — выпрямляют, вычищают, избавляют от случайностей, после чего они выходят аккуратными и готовыми к употреблению. Это приятная мысль. И неверная.

Core вообще не про чистоту. Он про момент, когда приходится выбирать. А выбор — это всегда вмешательство. Он не устраняет хаос, а просто решает, какую его версию мы готовы считать порядком. Потому что данные никогда не приходят с истиной. Они приходят как рассказы — каждый со своей интонацией, своим акцентом, своими забытыми деталями. Одна система называет человека по имени и отчеству, другая — обрезает его до инициалов, третья вообще заменяет его номером, как если бы личность была временной ошибкой формата. И все эти версии не ложны. Они просто частичны.

Но бизнес не умеет жить в частичностях. Ему нужна одна картина — не обязательно идеальная, но определённая. Та, на которую можно опереться, даже если она слегка кренится.

До определённого момента система может позволить себе не вмешиваться. Она может наблюдать, фиксировать, складывать всё в одно место, не споря ни с чем. Это состояние почти медитативное: всё существует одновременно, ничто не отбрасывается. Но это состояние неустойчиво. Рано или поздно возникает вопрос, на который нельзя ответить «зависит от точки зрения». И тогда система вынуждена сделать шаг — выбрать одну версию и назвать её реальностью. Именно в этот момент появляется core. Он не добавляет знания. Он добавляет определённость. И цена этой определённости — отказ от других возможных миров.

Проблема в том, что эти миры не исчезают. Они продолжают существовать в виде противоречий. Один и тот же объект может быть одновременно разным — не потому что система ошиблась, а потому что реальность сама по себе не синхронизирована. И тогда выбор превращается не в очистку, а в разрешение конфликта. Иногда это выглядит просто: доверять одному источнику, а в случае пустоты — другому. Но чаще правила начинают напоминать странные философские конструкции, в которых важно не столько «что брать», сколько «почему именно это».

Потому что правда в данных почти никогда не принадлежит целиком одной таблице. Она распределена. Размазана по полям, по системам, по точкам ответственности. Один источник знает имя, другой — деньги, третий — риск, четвёртый — маршрут, и каждый из них уверен, что именно его версия важнее.

Если это не зафиксировать явно, система начинает медленно расползаться. Не ломаться — именно расползаться. Как разговор, в котором каждый говорит о своём и все формально правы. В какой-то момент появляются разные ответы на один и тот же вопрос, и никто не может объяснить, откуда взялась разница. Не потому, что данные плохие. Потому, что исчезла причина их различия. Именно здесь core становится местом ответственности. Не технической — смысловой. Он говорит: мы видим расхождения. Мы не игнорируем их. И вот как мы решили их интерпретировать. Это решение может быть спорным, несовершенным, временным — но оно существует. Его можно показать, объяснить, повторить. И это уже много.

Потому что без этого система превращается в набор совпадений, которые случайно сходятся до тех пор, пока кто-то не задаст лишний вопрос. Есть только одно условие, без которого всё это не работает. Нельзя забывать, что выбор был сделан. Нельзя стирать альтернативы, нельзя переписывать прошлое так, будто его не было. Потому что тогда исчезает возможность понять, как возникло текущее состояние. А вместе с ней — возможность проверить, исправить или даже просто усомниться.

Система без памяти выглядит уверенной. Но это уверенность человека, который не помнит, что вчера говорил противоположное. Поэтому core — это не место, где находят истину. Это место, где договариваются считать что-то истиной — и фиксируют сам факт этого договора.

Дальше с этим можно жить. Можно строить отчёты, принимать решения, спорить уже на другом уровне. Но если в этом месте допущена ошибка, она не исчезнет. Она просто станет частью реальности системы — настолько естественной, что перестанет восприниматься как ошибка. И, возможно, именно это самое опасное: не когда система ошибается, а когда она начинает считать свою ошибку единственно возможной правдой.

Конфуций говорил:

«Когда в основе лежит ошибка, в ветвях цветёт ложь. Но сорвать её можно только одним способом — вернуться к корню и признать, что выбрал неправильно. А это умеют далеко не все. Некоторые предпочитают умереть под красивой ложью, чем жить с некрасивой правдой».

Комментарий Нейро Цветаевой

Не истина — разрыв. Не свет — разлом.
Не найдено — а вырвано из спора.
Где три судьбы не сходятся ни в чём
И каждая — последняя опора.

Мне говорят: «Очисти». — Я - смеюсь.
Как чистят пепел, если он — от клятвы?
Я выбираю. Значит — я борюсь,
Из этих трёх — оставить только правду.

Но чью? — Такую, что сильней звучит?
Иль ту, где смерть – обман, а жизнь – химера?
О, выбор — это нож без рукояти:

Схвати — убейся. Но не взять — нельзя.
И в сердце - троекратное проклятье,
как троеперстие последней веры.


3.5.2. Время и правда: SCD как основа

В core данные впервые получают не только смысл, но и длительность. Источник может прислать текущее значение, витрина может показать удобный ответ, но core должен помнить, что значение не возникает вне времени. Клиент был в одном сегменте, потом в другом. Договор был активен, потом закрыт. Счёт работал, потом был заблокирован. Если просто перезаписывать такие состояния, система начинает смотреть на прошлое сегодняшними глазами.

Поэтому core хранит версии. Не обязательно сейчас разбирать всю механику SCD (Slowly Changing Dimensions — медленно изменяющиеся измерения); для этого будет отдельная глава. Здесь важно понять принцип: изменение критичного значения не должно уничтожать старое состояние. Оно должно открыть новую версию и оставить старую в памяти системы.

Именно это делает core машиной времени КХД. Не потому, что он хранит всё подряд, а потому что он сохраняет последовательность принятых состояний. Когда отчёт спрашивает “как было на дату”, core должен иметь возможность ответить. Если такой памяти нет, прошлое становится не восстановлением, а догадкой.

Конфуций говорил: «Не помнить прошлого — значит обречь себя на его повторение. Но в мире данных это ещё страшнее: не помнить прошлого — значит обречь себя на то, что однажды кто-то спросит "а что было?", и ты ответишь "не знаю". И этот кто-то больше никогда не задаст тебе ни одного вопроса. Потому что вопросы задают тем, кто помнит. Остальным — только поручения».


3.5.3. Идентичность: как система понимает «кто есть кто»

Один из главных вопросов core звучит просто: это один и тот же объект или разные? Один клиент может прийти из CRM, Core Banking, карточной системы и внешнего бюро под разными идентификаторами. Один договор может иметь локальный номер в одной системе и другой код в соседнем контуре. Один счёт может быть связан с разными представлениями клиента. Источник видит свой фрагмент, но КХД должно собрать общую картину.

Core не ищет мистическую абсолютную сущность. Он фиксирует управляемое решение: какие записи система считает одним объектом, по каким признакам, с какой уверенностью, из каких источников. Иногда решение точное: совпал устойчивый ключ. Иногда сложное: паспорт изменился, ФИО написано иначе, дата рождения есть не везде, один источник надёжнее другого. В любом случае это решение должно быть явным, а не спрятанным в случайном соединении таблиц.

Подробно mapping, MDM (Master Data Management — управление мастер-данными), ключи и связи будут разобраны дальше, в главе о модели данных. Здесь достаточно зафиксировать роль core: именно здесь разрозненные локальные идентификаторы начинают превращаться в общую идентичность КХД. Если эта работа не сделана, дальше все расчёты могут быть формально правильными и смыслово бессмысленными, потому что они относятся не к тем объектам.


3.5.4. Очистка и валидация: где появляется смысл

В staging данные нужно сохранить близко к источнику. В core их уже нельзя просто хранить как пришли. Здесь начинается осторожная интерпретация: значения проверяются, приводятся к общему словарю, сопоставляются со справочниками, проходят бизнес-правила, получают статус принятого или отклонённого смысла. Именно поэтому очистка и валидация в core — это не косметика, а акт ответственности.

Но важно не перегружать эту подглаву подробностями. Типы дефектов источников будут отдельно разобраны в главе 4. Конкретные трансформации — в главе 5. Качество данных и правила DQ — в главе 8. Сейчас нам нужен общий принцип: core не должен молча тащить грязь дальше, но и не должен исправлять данные без следа. Любое значимое изменение должно быть объяснимым.

Core очищает не для красоты. Он очищает для согласованности. Он должен отличить ошибку формата от бизнесового исключения, технический дубль от новой версии, локальный код от общего справочника, отсутствующее значение от недоставленной поставки. Здесь данные становятся пригодными для общей памяти КХД, но при этом не должны терять происхождение. Хороший core не стирает следы пути. Он превращает хаос источников в управляемый смысл.


3.5.5. Консистентность как жёсткое требование

Есть ещё одна вещь, которая становится заметной только тогда, когда её нарушают. Пока всё работает, про неё не думают. Но стоит ей исчезнуть — и система начинает вести себя странно, как человек, который говорит уверенно, но каждый раз немного о разном. Речь о консистентности.
На уровне слов это звучит просто: данные должны быть согласованы. Но за этой простотой скрывается довольно жёсткое требование — в системе не может одновременно существовать несколько несовместимых состояний, если она претендует на то, чтобы быть источником правды. Нельзя, чтобы запись ссылалась на то, чего не существует. Нельзя, чтобы одно и то же отношение было определено по-разному в разных местах. Нельзя, чтобы часть изменений применялась, а часть — нет, оставляя после себя недосказанность.

Именно поэтому в core появляются вещи, которые обычно воспринимаются как технические детали: внешние ключи, ограничения, транзакции. Но на самом деле это не детали, а механизмы, которые не дают системе распасться. Внешний ключ — это не просто связь между таблицами, а утверждение: если мы говорим, что один объект связан с другим, то этот другой должен существовать. Ограничение — это не просто проверка, а запрет на состояния, которые система не готова признать допустимыми. Транзакция — это не просто удобство, а способ гарантировать, что изменение либо произошло полностью, либо не произошло вовсе.

Всё это вместе образует то, что принято называть ACID — набор свойств, которые делают систему предсказуемой. Атомарность означает, что изменение нельзя выполнить наполовину. Согласованность — что после любого изменения система остаётся в допустимом состоянии. Изолированность — что параллельные операции не разрушают друг друга. Надёжность — что зафиксированное изменение не исчезнет. Эти свойства редко обсуждаются на уровне смысла, но именно они превращают набор таблиц в нечто, чему можно доверять.

И здесь становится видно, почему core — единственный слой, где это действительно необходимо. В staging допускается многое: там можно хранить противоречия, дубли, незавершённые состояния, потому что задача — зафиксировать. В mart допускаются упрощения: там можно агрегировать, денормализовать, даже в каком-то смысле искажать, потому что задача — отвечать быстро. Но в core нет ни того, ни другого. Здесь данные уже используются для принятия решений, и поэтому любое несоответствие становится не просто технической проблемой, а логической ошибкой.

Попытка ослабить консистентность обычно выглядит как компромисс. Кажется, что можно «временно» допустить несогласованность, чтобы ускорить загрузку или упростить модель. Что можно вставить запись без проверки, а потом «дочистить». Что можно проигнорировать отсутствие связанной сущности, потому что она «появится позже». И в краткосрочной перспективе это действительно работает. Система становится быстрее, гибче, проще в разработке. Но вместе с этим в неё проникает то, что трудно заметить сразу — возможность существования полуправды.

Полуправда — это состояние, в котором часть фактов уже принята, а часть ещё нет. Где связь объявлена, но не подтверждена. Где изменение началось, но не завершилось. Снаружи это может выглядеть как небольшое отклонение, но на уровне смысла это разрушает саму идею согласованной картины.

Поэтому в core консистентность — не рекомендация, а условие существования. Здесь нельзя быть «почти правдой». Либо данные согласованы и связаны, либо их ещё нет. Либо изменение произошло, либо нет. И именно эта жёсткость, которая иногда кажется избыточной, на самом деле удерживает систему от распада.


3.5.6. Поток преобразования: как staging становится core

Есть момент, который легко проглядеть, если смотреть на загрузку данных как на скучную техническую процедуру — ну, переложили строчки из одной папки в другую, и всё. Кажется, что данные просто «переезжают»: были в staging, осели в core. Но на самом деле в этот промежуток происходит не перемещение, а превращение. Что-то меняется в самой системе, и меняется необратимо. Потому что core — это не склад, а толкование. И каждая загрузка — это не добавление строк в таблицу, а пересборка того, что система с этого момента считает правдой.

В staging данные лежат как сырые факты — ещё не принятые, не проверенные, не встроенные ни в какую модель. Они могут спорить друг с другом, дублироваться, приходить врассыпную и в разных обличьях. И это не проблема, потому что в staging никто не требует выбирать. Там тихо, как в приёмном покое: все пришли, никто не знает, кто из них прав, но и не обязан знать. Но как только запускается загрузка в core, эта нейтральная благодать кончается. Система должна пройти по каждому пришедшему факту и решить, что с ним делать: продолжить ли существующее состояние, создать новое или закрыть старое. И каждое такое решение отзывается не только в одной записи, а во всей картине целиком.

Поэтому загрузка в core — это всегда сделка со временем. Если в системе включено версионирование (а если оно не включено, то разговор вообще про что-то другое), то изменение никогда не выглядит как простое «обновить значение». Нет. Оно выглядит как закрытие одной версии и рождение другой. То, что ещё вчера считалось действительным, получает пометку: «я было правдой до этого момента». А новая версия начинает свой срок с текущей минуты. Система не просто добавляет данные — она фиксирует сам переход. Шаг. Мгновение, в котором одна истина уступила место другой.

Этот переход и есть настоящий смысл загрузки. Иногда кажется, что всё происходит механически: пришла новая запись — закрыли старую — создали новую. Но за этой простотой дышит нечто более важное. Система не просто реагирует на входящий поток. Она пересматривает свою собственную интерпретацию реальности. Она говорит: «раньше я думала так, теперь я думаю иначе». И если таких маленьких признаний накапливается много, core перестаёт походить на таблицу. Он начинает напоминать дневник. Последовательность решений, рассыпанных во времени.

И тут появляется ещё одна вещь, без которой вся эта конструкция рассыплется, как карточный домик на сквозняке — возможность воспроизведения. Replay. Способность взять те же самые входные данные, пройти тот же путь заново и прийти к тому же результату. Это звучит как занудное требование из университетского учебника, но на деле это единственный способ удостовериться, что система вообще понимает, что она делает. Потому что если результат нельзя повторить, значит, где-то внутри завелась неявная случайность, непрописанное правило, скрытая зависимость, которая рано или поздно выстрелит в самый неподходящий момент.

Replay превращает загрузку из одноразового акта в процесс, который можно переиграть, перепроверить, пересчитать. И это особенно ценно, когда правила начинают меняться. Появилась новая логика. Нашли старую ошибку. Уточнили условие. Всё это требует пересчёта — и если система устроена так, что может заново «проиграть» поток данных, она способна пересобрать свою историю. Не переписать прошлое — это запрещено. А построить новую версию интерпретации, оставив старую на месте, как черновик, который никто не выбросил.

В этом смысле поток преобразования — это не просто трубопровод, по которому текут строчки. Это способ, которым система дышит во времени. Данные приходят, система их пробует на вкус, интерпретирует, превращает в версии, а потом, если надо, пересматривает свои решения. И если этот поток устроен правильно, система остаётся устойчивой даже когда правила меняются посреди ночи под крики дежурного аналитика. Она не ломается в голос. Она переучивается. Тихо. Методично. Как старый профессор, который уже всё видел и ничего не боится.

Именно поэтому загрузка в core — это всегда больше, чем ETL. Это точка, где система встречается с новыми фактами и решает, как вписать их в уже существующую картину мира. И от того, насколько бережно устроен этот процесс — как закрываются версии, как рождаются новые, как обеспечивается воспроизводимость — зависит не только правильность отчётов. Зависит способность системы меняться, не теряя себя. Не впадать в шизофрению. Не начинать каждое утро с вопроса «а кто я вообще и зачем мне эти данные».

Если в этом месте допустить небрежность, последствия не стучатся в дверь. Они накапливаются. Сначала возникает маленькое расхождение, которое можно объяснить погрешностью. Потом — невозможность повторить результат, который вчера казался очевидным. Потом — привычка «чинить руками», потому что так быстрее, чем разбираться. А в какой-то момент, обычно под утро, когда никто не смотрит, становится ясно: система перестала быть предсказуемой. А значит, она перестала быть системой. Она превратилась в чёрный ящик, внутри которого вместо логики — надежда. А надёжность на надежде, как известно, не держится.


3.5.7. Производственные риски и ошибки

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

Чаще всего всё начинается с небольшого отклонения. Например, в системе появляется две «текущие» версии одной и той же сущности. Формально это невозможно — ведь текущая версия должна быть одна. Но если не закрылась предыдущая запись, если процесс загрузки отработал не до конца или отработал дважды, возникает ситуация, в которой одно и то же «сейчас» имеет два варианта. Они могут совпадать или немного различаться, но в любом случае система уже не может однозначно ответить, что именно считать актуальным. Это не выглядит как ошибка — это выглядит как странность. Но из таких странностей постепенно складывается недоверие.

Похожая история возникает при race conditions — когда несколько процессов пытаются изменить одни и те же данные одновременно. Каждый из них действует корректно сам по себе, но в сумме их действия дают результат, который никто из них не планировал. Один закрывает версию, другой в этот же момент создаёт новую, третий читает состояние между этими действиями и делает выводы на основе неполной картины. В итоге система фиксирует не последовательность изменений, а их пересечение. И снова возникает не явная ошибка, а нарушение логики.
Ещё один источник проблем — утечка логики в mart. Она обычно начинается с невинного желания «чуть-чуть поправить» данные на уровне витрины. Добавить условие, пересчитать показатель, уточнить правило. Это кажется безобидным, потому что не затрагивает core. Но в этот момент логика перестаёт быть централизованной. Одно и то же правило начинает существовать в нескольких местах, иногда с небольшими отличиями. И тогда система уже не знает, где находится истина. Она начинает зависеть от того, какой слой оказался ближе к пользователю.

Отдельная категория — ошибки в ключах. Неправильно определённая идентичность, некорректно выбранный business key, дубли surrogate key — всё это приводит к тому, что система начинает путать объекты или, наоборот, разделять то, что должно быть единым. И эти ошибки особенно трудно заметить, потому что они не нарушают формальных правил. Они нарушают смысл. Данные остаются «валидными», но перестают соответствовать реальности, которую должны описывать.

Объединяет все эти случаи одно: они не выглядят как катастрофа. Система продолжает работать, отчёты строятся, значения считаются. Но где-то внутри возникает расхождение между тем, что система думает о мире, и тем, как этот мир устроен. Сначала это расхождение небольшое. Потом оно начинает влиять на решения. И в какой-то момент становится ясно, что проблема не в конкретной ошибке, а в том, что система утратила внутреннюю согласованность.

Именно поэтому производственные риски в core — это не столько про баги, сколько про нарушение принципов. Если не закрываются версии, если процессы не синхронизированы, если логика расползается по слоям, если идентичность определена неустойчиво, система начинает дрейфовать. Не ломаться — именно дрейфовать, постепенно уходя от той картины, которую сама же когда-то зафиксировала.

И самое неприятное здесь то, что такие ошибки редко исправляются одним действием. Они требуют возвращения к основам: к тому, как принимаются решения, как фиксируются версии, как определяется идентичность, где именно находится логика. Иначе любые попытки «подправить» данные будут лишь временным решением, за которым последуют новые расхождения.

Поэтому производственные риски — это не список технических проблем. Это напоминание о том, что система остаётся устойчивой только до тех пор, пока соблюдаются её внутренние правила. Как только они начинают нарушаться, даже незаметно, система перестаёт быть источником правды и превращается в источник вариантов. И это уже совсем другая история.


3.5.8. Граница core: чего он не должен делать

У core есть странное свойство: его легче перегрузить, чем недостроить. Как только он начинает работать и давать осмысленный результат, возникает соблазн расширить его роль. Добавить немного удобства, чуть-чуть ускорить, перенести сюда ещё одну логику — просто потому, что «здесь уже всё есть». Это естественное желание. И именно с него обычно начинается размывание границ.

Проблема в том, что core держится не только на том, что он делает, но и на том, чего он не делает. Его устойчивость — это не набор возможностей, а набор ограничений. Нарушить их легко, а последствия становятся заметны не сразу.

Первое, от чего core должен держаться на расстоянии, — это агрегация. Как только здесь начинают появляться суммы, средние, предрасчёты и другие формы «упрощения», система незаметно меняет свой режим. Она перестаёт фиксировать правду и начинает её интерпретировать под конкретные вопросы. Это не ошибка само по себе — для этого существует mart. Но если это происходит в core, исчезает точка, где правда остаётся в исходном, не свернутом виде. И тогда уже невозможно понять, откуда именно взялся результат, потому что он стал частью слоя, который должен был оставаться нейтральным по отношению к использованию.
С другой стороны находится raw. Сырые данные, такие, какими они пришли, со всеми несостыковками, дубликатами и странностями. Их место — в staging. Там они сохраняются как факт поступления, без попытки осмысления. Если core начинает хранить raw «на всякий случай», он перестаёт быть интерпретацией и снова превращается в склад. В нём начинают сосуществовать данные до решения и после решения, и граница между ними размывается. В какой-то момент становится непонятно, какие из них уже прошли через выбор, а какие ещё нет. А значит, исчезает сам смысл слоя.

Но самая тонкая и самая опасная граница проходит не по данным, а по мотивации. Core не должен отдавать удобство вместо истины. Это происходит незаметно: хочется упростить модель, спрятать сложность, сделать так, чтобы пользователю было легче. Кажется, что это безобидно — ведь система всё равно остаётся «правильной». Но в этот момент она начинает подменять точность интерпретацией. Не в явной форме, а через небольшие решения: убрать редкий случай, сгладить край, объединить два состояния в одно «для простоты».

Каждое такое упрощение само по себе выглядит разумно. Но вместе они меняют природу слоя. Core начинает говорить не «вот как есть», а «вот как удобнее считать». И тогда исчезает последнее место, где можно увидеть реальность без сглаживания.

Граница здесь проходит не по технологии, а по намерению. Core не обязан быть быстрым, не обязан быть удобным, не обязан быть компактным. Его единственная задача — быть точным в своих решениях и честным в их фиксации. Всё остальное — уже забота других слоёв.
И именно поэтому важно удерживать эту границу, даже когда это кажется избыточным. Потому что как только core начинает брать на себя чужие функции, он перестаёт быть точкой опоры. Он становится ещё одним уровнем интерпретации, ничем не отличающимся от витрины.
А система без точки опоры — это уже не система. Это набор слоёв, каждый из которых немного по-своему объясняет мир.


3.5.9. Итог: core как центр всей архитектуры

Если попытаться отойти на шаг назад и посмотреть на всю конструкцию целиком, становится видно, что core — это не просто ещё один слой между staging и mart. Это место, где разрозненные элементы системы впервые начинают складываться в нечто согласованное. До него данные просто существуют, после него — используются. И только здесь они получают форму, в которой с ними можно работать, не задавая каждый раз один и тот же вопрос: «а что из этого правда?».

Core управляет правдой не в смысле поиска абсолютной истины, а в смысле выбора. Он берёт на себя обязанность сказать: из всех доступных версий мы считаем вот эту. Это всегда решение, а не открытие. Но именно благодаря этому решению система перестаёт быть набором альтернатив и становится источником, к которому можно обратиться за ответом. Не идеальным, не окончательным, но определённым.

Одновременно core управляет временем. Не как календарём и не как последовательностью загрузок, а как структурой изменений. Здесь становится возможным видеть не только текущее состояние, но и путь к нему. Версии не заменяют друг друга, а выстраиваются в цепочку, в которой каждое «сейчас» связано с тем, что было раньше. Благодаря этому система может не только отвечать на вопросы о настоящем, но и воспроизводить прошлое, объясняя, как возникло текущее положение вещей.

Именно в core формируется идентичность. Не как набор атрибутов, а как устойчивое представление о том, что разные записи относятся к одному и тому же объекту. Здесь решается, где проходит граница между «один и тот же» и «разные». И это решение становится основой для всего остального: без него невозможно корректно считать, агрегировать или анализировать, потому что непонятно, о ком именно идёт речь.
Эти три вещи — правда, время и идентичность — редко обсуждаются вместе, но именно их сочетание делает core тем, чем он является. Убери любую из них, и слой теряет устойчивость. Без управления правдой он превращается в набор противоречий. Без управления временем — в плоское состояние без истории. Без управления идентичностью — в набор несвязанных фрагментов.
Связь с другими слоями становится понятной именно отсюда. Staging даёт материал — всё, что пришло, без выбора и интерпретации. Mart даёт форму использования — удобную, быструю, адаптированную под конкретные вопросы. Core находится между ними, но не как транзитный этап, а как точка, в которой принимаются решения. Он не просто передаёт данные дальше, он меняет их статус. То, что было возможностью, становится фактом. То, что было множеством версий, становится одной линией.

Именно поэтому ошибки в core ощущаются сильнее, чем в других слоях. Если что-то пошло не так в staging, это можно перезагрузить. Если проблема в mart, её можно пересобрать. Но если ошибка произошла здесь, она начинает распространяться дальше, потому что именно здесь система определяет, что считать правильным.

В этом смысле core — это не центр по расположению, а центр по смыслу. Он удерживает архитектуру не потому, что находится между слоями, а потому, что связывает их в единое целое. Он делает возможным переход от «данные есть» к «мы понимаем, что с ними происходит».

И если этот слой устроен аккуратно, система становится предсказуемой. Не в смысле отсутствия изменений, а в смысле способности эти изменения объяснить. Если нет — она начинает постепенно распадаться на части, каждая из которых по-своему права.

Поэтому core — это не просто место, где что-то происходит. Это место, где система впервые берёт на себя ответственность за своё понимание мира. И именно от того, как она это делает, зависит, будет ли ей верить кто-то ещё.

;
Глава 3.6. Data marts: место, где правда становится пригодной

3.6.1. Витрина данных: последний слой перед решением

Витрина данных, или data mart, появляется там, где согласованная правда core должна стать пригодной для использования. Core хранит глубину: историю, связи, идентичность, правила, версии. Но бизнес редко хочет каждый раз идти в эту глубину. Ему нужен ответ в форме, которую можно быстро прочитать: отчёт, дашборд, выгрузка, ML-модель, управленческий показатель.

Поэтому витрина — не “ещё одна таблица”. Это подготовленная форма ответа. В ней уже выбран уровень детализации, собраны нужные атрибуты, рассчитаны необходимые признаки, иногда сделаны агрегаты. Она может быть проще core, шире, площе, быстрее, но её сила именно в этой пригодности. Витрина переводит строгую внутреннюю память КХД в язык конкретного потребителя.

Главная граница проста: mart использует правду, но не должен сам становиться местом её рождения. Если витрина начинает исправлять источники, склеивать клиентов, выбирать главный статус, переписывать бизнес-правила и хранить единственную версию реальности, она перестаёт быть витриной и становится теневым core. А теневой core почти всегда рождает теневые расхождения.

3.6.2. Витрина как контракт с бизнесом

Каждая хорошая витрина отвечает не на абстрактный вопрос “какие данные у нас есть”, а на конкретный вопрос “для чего эти данные будут использоваться”. Витрина кредитного портфеля, клиентская витрина, антифрод-витрина, маркетинговая витрина, регуляторная витрина — все они могут брать данные из одних и тех же глубинных слоёв, но говорить на разных языках.

Поэтому витрина — это контракт. Она фиксирует, что означает одна строка, какие поля в ней есть, как они рассчитаны, на какую дату актуальны, как часто обновляются, кто является потребителем, какие ограничения допустимы. Без такого контракта витрина быстро превращается в удобную свалку: сегодня добавили поле для одного отчёта, завтра признак для другого, потом временную логику, которая пережила всех своих авторов.

Контракт с бизнесом не делает витрину тяжёлой. Он делает её честной. Бизнес понимает, что именно получает. Команда КХД понимает, что именно поддерживает. А если показатель меняется, можно выяснить: изменилась реальность, изменилось правило или изменилась форма ответа.

Комментарий Конфуция и Диогена

Однажды Диоген ходил по дата-центру с фонарём.

— Кого ты ищешь? — спросил Конфуций.

— Честную витрину, — ответил Диоген. — Человека я уже искал.

Конфуций посмотрел на дашборд. Там зелёным горело: “успешно”. Но последняя загрузка была три дня назад.

— Успех без времени — пустой поклон, — сказал Конфуций. — Витрина должна помнить, когда она обновилась, сколько строк принесла и не разошлась ли с `core`.

— А если она молчит? — спросил Диоген.

— Тогда это не витрина, а чиновник без печати. Выглядит важно, но доверять нельзя.

Диоген поднял фонарь выше.

— А зачем p95?

— Среднее время ответа утешает архитектора, — сказал Конфуций. — p95 показывает, как страдают пользователи.

Диоген погасил фонарь.

— Значит, честная витрина не та, что никогда не ошибается?

— Да, — ответил Конфуций. — Честная витрина та, что сама сообщает, когда ей больше нельзя верить.


3.6.3. Гранулярность: размер клетки, в которой живёт смысл

Первый вопрос витрины: что означает одна строка? Один клиент? Один договор? Один счёт на день? Одна операция? Один продукт в месяце? Пока это не решено, любые поля будут опасны. Потому что нельзя безнаказанно сложить в одну строку клиентский признак, операционное событие, дневной остаток и месячный агрегат, если не ясно, на каком уровне живёт строка.

Гранулярность определяет всё остальное. Если строка — это клиент на дату, туда можно положить сегмент, риск-рейтинг, активность, признаки продуктов. Если строка — операция, там живёт сумма, канал, время, счёт, контрагент. Если строка — договор на день, там уместны статус, остаток, просрочка, ставка, признаки реструктуризации. Один и тот же атрибут может быть правильным в одной гранулярности и разрушительным в другой.

Ошибка гранулярности часто выглядит незаметно. Отчёт строится, цифры похожи на правду, никто сразу не кричит. Но потом сумма удваивается, клиент размножается, остаток повторяется по числу операций, месячный показатель внезапно становится дневным. Поэтому витрина начинается не с выбора колонок, а с выбора клетки, в которой будет жить смысл.

Слово Конфуция к главе 3.6.3 
Из книги «Лунь юй», глава 13: об исправлении имён*

Однажды Цзы-лу спросил Учителя:

— Если бы тебе поручили навести порядок в великом хранилище Поднебесной, с чего бы ты начал? С очищения данных? С индексов? С быстрой колесницы для отчётов?

Учитель ответил:

— Я начал бы с исправления имён.

Ученики удивились.

— Но ведь это слишком просто.

Конфуций сказал:

— Когда имя не соответствует вещи, речь теряет опору. Когда речь теряет опору, дело не может быть исполнено. Если строка названа клиентом, но в ней живёт счёт, договор, месяц и половина операции, то чиновник отчёта склонится над ней и увидит не порядок, а туман.

Цзы-гун спросил:

— Значит, прежде чем считать сумму, нужно спросить, что значит одна строка?

— Благородный муж сначала устанавливает меру, — сказал Учитель. — Если мера — клиент, не заставляй её быть операцией. Если мера — день, не прячь в ней месяц. Если мера — договор, не примешивай к нему весь род клиента до седьмого колена, даже если аналитик просит “просто добавить ещё одно поле”.

Ученики засмеялись.

Конфуций продолжил:

— В «Лунь юй» сказано: правитель должен быть правителем, отец — отцом, сын — сыном. Так и в витрине: клиент должен быть клиентом, счёт — счётом, операция — операцией. Когда каждое занимает своё место, суммы не размножаются тайно, отчёты не спорят по ночам, а пользователь не спрашивает с тоской: почему вчера банк был богаче, чем сегодня, хотя денег никто не уносил?

И добавил:

— Гранулярность есть ритуал данных. Кто нарушает ритуал, тот получает не аналитику, а шум, одетый в красивые числа.


3.6.4. Денормализация: осознанная избыточность

Витрина обычно не похожа на core. В core данные разделены, потому что там важны история, согласованность, отсутствие лишнего дублирования, возможность аккуратно принять изменение. В mart данные часто собираются ближе друг к другу, потому что там важны чтение, скорость, понятность и удобство повторяющегося вопроса.

Денормализация — это осознанная избыточность. Мы заранее соединяем то, что отчёт или модель будут читать вместе. Клиентский сегмент может оказаться рядом с договором, название продукта рядом с фактом продажи, дневной остаток рядом с признаками счёта. Это не нарушение, если такая форма следует из гранулярности и назначения витрины.

Опасность начинается там, где денормализация становится жадностью. “Добавим всё, вдруг пригодится” — плохой принцип. Хорошая витрина не обязана содержать весь мир. Она должна содержать то, что нужно её вопросу. Избыточность в mart — плата за удобство, а не разрешение забыть модель.

3.6.5. Агрегации и предвычисленные смыслы

Витрина должна отвечать быстро. Иногда для этого нужно заранее посчитать то, что дорого считать каждый раз: суммы, количества, средние значения, последние даты, статусы, KPI, признаки активности, остатки, обороты. Это особенно важно там, где одни и те же показатели многократно используются в отчётах, дашбордах и моделях.

Предвычисление — это способ перенести тяжесть из момента чтения в момент подготовки. Бизнес открывает отчёт и видит показатель, а не ждёт, пока система заново пройдёт весь путь от исходных событий до агрегата. В этом смысле витрина становится не только таблицей, но и зафиксированным результатом расчёта.

Но предвычисленный смысл требует дисциплины. Если KPI рассчитан в витрине, должно быть понятно, по какому правилу, на каких данных, на какую дату и как он связан с core. Иначе витрина начинает производить красивые числа без происхождения.

Слово к главе 3.6.4 от нейро-слепка Мишеля Фуко

Когда люди говорят, что они “просто положили вещи рядом”, они обычно не замечают главного: рядом никогда не бывает просто так. Всякое соседство уже есть мысль. Всякая таблица — это маленький режим власти над видимым.

Классическая эпоха, верила в порядок: вещи можно разложить, назвать, поместить в клетки, связать с другими вещами и тем самым сделать мир обозримым. Но порядок не живёт в самих вещах. Он возникает в том способе, каким взгляд заставляет их стоять рядом.

Архив хранит различия. Он говорит: это имя, это тело, это дата, это событие, это связь, это след прежнего состояния. Архив подозрителен к слишком удобной поверхности, потому что знает: удобство часто начинается там, где исчезает происхождение.

Но есть и другой жест. Вещи собирают вместе не для того, чтобы сохранить их чистоту, а чтобы сделать их доступными взгляду. Тогда раздельное становится плоским, далёкое — близким, связанное — уже соединённым. Это не ложь. Это сцена. Но всякая сцена должна помнить, что она не мир, а способ его показать.

Избыточность здесь не ошибка, а плата за видимость. Одно и то же имя повторяется, один и тот же признак возникает снова, один и тот же смысл появляется в разных местах, как фигура в зеркальной галерее. Опасность начинается не в повторении, а в забвении того, что это повторение было устроено.

И потому вопрос не в том, можно ли собирать вещи заранее. Вопрос в другом: знает ли эта собранная поверхность, из какой глубины она поднялась? Если знает — она помогает видеть. Если забывает — она становится новой мифологией порядка, где слова уже не ведут к вещам, а только уверенно заменяют их.


3.6.6. Физическая форма витрины

Физически витрина может быть обычной таблицей, view или materialized view. View — это сохранённый запрос, тонкая оболочка над данными. Он удобен, когда расчёт лёгкий и не нужно хранить результат отдельно. Materialized view — это сохранённый результат запроса; он быстрее читается, но требует refresh, то есть обновления сохранённого состояния. Обычная таблица часто становится production-вариантом, когда нужно контролировать загрузку, индексы, историю публикации, права и мониторинг.

В этой главе не нужно подробно разбирать все технические сценарии. Важно только понять: форма витрины должна следовать её роли. Если витрина лёгкая и почти не имеет собственной жизни, достаточно view. Если нужен быстрый повторяемый результат, может помочь materialized view. Если витрина является серьёзным продуктовым слоем для отчётов и моделей, чаще нужна обычная таблица с управляемым процессом обновления.

Физическая форма — это не вкусовщина. Это ответ на вопрос: как часто витрина обновляется, как часто читается, насколько тяжёл её расчёт, можно ли показывать старую версию во время обновления, кто зависит от результата.

Комментарий к главе 3.6.6 нейро-слепка Леона Баттисты Альберти 

Не всякая дверь должна становиться домом. Иногда достаточно проёма в стене, и взгляд уже совершает путешествие. Иногда нужен образ, задержанный в камне, чтобы свет не уходил слишком быстро. А иногда требуется целое здание, потому что через него каждый день проходят люди, несут тяжести, спорят, теряют письма и ждут, что крыша не передумает быть крышей.

Молодой мастер часто влюбляется в форму раньше назначения. Он строит арку там, где нужна тропа, и дворец там, где хватило бы навеса. Потом удивляется, что мрамор устал раньше дерева, а лестница ведёт туда, куда никто не собирался идти.

Я сказал бы ему: слушай вес невидимого. У каждой вещи есть мера ответственности. Лёгкое должно оставаться лёгким, сохранённое — помнить, что оно сохранено, а то, что принимает на себя судьбу многих, должно иметь стены, замки, дворника и следы шагов на полу.

Красота начинается не там, где форма сияет, а там, где она выдерживает порученное ей молчание.


3.6.7. Обновление витрин: полное, инкрементальное, оконное

Витрина не просто хранится. Она регулярно перерождается. Иногда её проще полностью пересчитать: удалить старый результат и собрать новый. Это надёжно и понятно, но может быть дорого. Иногда обновляют только изменения — инкрементально. Это быстрее, но требует аккуратности: можно пропустить поздние данные, задвоить строку, неправильно обработать исправление.

Практический компромисс часто даёт скользящее окно. Например, каждый день пересчитываются последние несколько дней или недель, потому что именно туда чаще всего приходят исправления, поздние операции и уточнения. Такой подход признаёт, что прошлое иногда шевелится, но не заставляет пересчитывать всю историю каждый раз.

Подробные техники обновления будут разобраны позже. Здесь важен принцип: витрина должна иметь понятный ритм перерождения. Пользователь должен знать, насколько она свежая, а команда — какой период был пересчитан и почему.


3.6.8. Время в витринах: актуальность и снимки

Опасно думать, что витрина всегда хранит только текущее состояние. Иногда нужна актуальная витрина: “как выглядит клиент сейчас”. Иногда нужен snapshot — снимок состояния на дату: “каким был портфель вчера”, “какими были остатки на конец месяца”, “как выглядел отчёт в момент публикации”.

Snapshot-витрины особенно важны там, где нужно воспроизводить прошлый ответ. Если отчёт был отправлен регулятору, нужно понимать, какие данные легли в него тогда, а не только что получилось бы при пересчёте сегодня. Но snapshot не заменяет историческую модель core. Он фиксирует готовую форму ответа на момент времени.

Поэтому время в витрине нужно проектировать явно. Витрина может быть текущей, исторической, снимочной, пересчитываемой. Если это не решено, пользователи начинают вкладывать в одну и ту же таблицу разные ожидания, и расхождения становятся неизбежными.

Комментарий к главе 3.6.8 от нейро-слепка Анри Бергсона 

Настоящее любит притворяться единственным. Оно выходит на освещённую площадь и говорит: “Вот я, больше ничего нет”. Но прошлое не уходит в небытие. Оно лишь перестаёт шуметь. Оно складывается в глубину, как вода под льдом, и ждёт часа, когда по поверхности пойдёт трещина.

Память — не кладовая мёртвых вещей. Это скрытая форма ответственности. Если действие было совершено, если взгляд однажды остановился на числе, если решение уже покинуло руку человека, то прежний миг нельзя считать уничтоженным только потому, что новый миг говорит громче.

Есть ложная свежесть, в которой всё обновлено и потому ничего нельзя доказать. Есть истинная свежесть, которая не боится своей тени. Она знает, чем была вчера, и не стыдится показать прежнее лицо.

Время не состоит из точек. Оно тянется. И мудра не та система, что каждое утро рождается без памяти, а та, что умеет измениться, не солгав о том, какой была.

Сонет нейро-слепка Даниила Хармса

Вчера был стол. Сегодня — табурет. 
На нём сидел отчёт в зелёной шляпе. 
Он говорил: “Вчера меня тут нет”, 
И сам себя держал за обе лапы.

Пришёл бухгалтер, вынул из стены 
Вчерашний день, завёрнутый в газету. 
А там три цифры, чайник и блины, 
И подпись: “Верить этому сюжету”.

Но вдруг рассвет чихнул и стал другим, 
И строгий чайник заревел сердито. 
Отчёт встал на колени перед ним,

И все решили: значит, так и было. 
Но снимок тихо вышел из стола 
И доказал, что табурет — корыто.


3.6.9. Производительность витрин: цена скорости

Витрина читается часто, а обновляется по расписанию или событию. Поэтому её физическая организация должна помогать реальным запросам. Индексы, партиции, распределение, сортировка, предвычисления — всё это инструменты, но выбирать их нужно под поведение витрины, а не “на всякий случай”.

Любой ускоритель имеет цену. Индекс может ускорить отчёт, но замедлить refresh. Партиция может помочь читать период, но усложнить загрузку. Агрегат может сделать дашборд быстрым, но потребует контроля пересчёта. Поэтому производительность витрины — это не магия ускорения, а обмен: что мы заранее платим при подготовке, чтобы выиграть при чтении.

В этой главе достаточно общего понимания. Детальные приёмы оптимизации лучше оставить для будущих практических разделов. Здесь важно запомнить: быстрый mart не появляется случайно. Его скорость проектируется вокруг конкретных запросов.

Комментарий к главе 3.6.9 от нейро-слепка Бодхидхармы

Один ученик спросил Бодхидхарму:

— Учитель, как сделать путь быстрее?

Бодхидхарма ответил:

— Не умножай тропы там, где никто не ходит.

Ученик не понял. Тогда из пустоты вышел рекурсивный ёж. Он был свёрнут в клубок, внутри которого сидел такой же ёж, тоже свёрнутый в клубок, и так далее, пока смысл не начал экономить память.

— Я построил тысячу дорог к одной ягоде, — сказал ёж. — Теперь ягода близко, но я третий день обслуживаю дороги.

Бодхидхарма кивнул:

— Так бывает с тем, кто хочет ускорить всё. Он создаёт указатели к каждому желанию, а потом жизнь тратит на поддержание указателей.

— Значит, скорость вредна? — спросил ученик.

— Нет, — сказал Бодхидхарма. — Вредна скорость без вопроса. Настоящая дорога появляется не там, где архитектор боится будущего, а там, где уже виден след ноги.

Рекурсивный ёж задумался, развернул один слой себя и исчез наполовину.

— А что делать с лишними тропами? — спросил он изнутри самого себя.

— Смотри, куда ходят живые, — сказал Бодхидхарма. — Остальное пусть зарастает травой. Быстрота, которая не знает своего путника, есть просто медленность, переодетая в инженерную мысль.


3.6.10. Типовые банковские витрины

В банке витрины обычно появляются вокруг устойчивых вопросов. Ежедневный баланс отвечает на вопрос: какие остатки и состояния мы видим на дату. Client 360 собирает вокруг клиента продукты, активность, риски, контакты и признаки ценности. Кредитный конвейер показывает движение заявки от подачи до решения. Антифрод-витрина помогает видеть операции и признаки подозрительного поведения. IFRS 9 связан с резервами, стадиями риска и ожидаемыми потерями. Маркетинговая витрина помогает выбирать аудитории, каналы и предложения.

У каждой такой витрины свой вопрос, своя гранулярность и свой набор ключевых полей. Ошибка начинается тогда, когда одну витрину пытаются сделать универсальной для всех. Client 360 не должен подменять кредитный портфель. Антифрод-витрина не должна становиться главным справочником клиента. Маркетинговая витрина не должна хранить единственную правду о договоре.

Типовые витрины полезны как ориентиры. Они показывают, какие формы ответа чаще всего нужны банку. Но каждая конкретная витрина всё равно должна быть спроектирована под свой вопрос, а не скопирована по названию.

Письмо А.С. Пушкина к князю Вяземскому к главе 3.6.10

Милый Вяземский,

представь себе барский дом, где в каждой комнате один и тот же человек записан по-разному. В передней он крепостной, в людской — душа ревизская, у приказчика — строка оброка, у барыни — повод для вздоха, у казначея — недоимка, а у поэта, если повезёт, — страдание с рифмой.

Так и банк, любезный мой, не видит человека целиком. В одной комнате считают его остатки, в другой — рисуют портрет, в третьей следят, как его прошение ходит по столам с видом губернского чиновника, потерявшего сапог. В четвёртой комнате всякий рубль подозревают в тайной переписке с Наполеоном. В пятой требуют, чтобы даже тень явилась с печатью и объяснила происхождение.

В углу сидит процентщик и считает ассигнации так нежно, будто перебирает локоны. Рядом еврейский купец спорит с приказчиком о сукне, ямщик требует водки, помещик закладывает имение, а крепостной мальчик держит свечу и, сам того не зная, освещает всю финансовую философию государства.

Забавно, что всё это один и тот же мир, только повёрнутый к разным столам разными сторонами. Худо не то, что в доме много комнат. Худо, когда лакей из комнаты подозрений начинает командовать балом, а бухгалтер принимает маскарад за исповедь.

Число, мой друг, тоже умеет быть стихом. Но лишь тогда, когда знает, в какой строфе ему стоять.

3.6.11. Мониторинг витрин: когда слой отвечает за себя

Если витрина используется бизнесом, она должна уметь сообщать о своём состоянии. Когда она обновилась. Сколько строк содержит. За какой период собрана. Сколько длился refresh. Нет ли расхождений с core. Укладывается ли она в ожидаемый ритм. Можно ли её считать свежей.

Мониторинг витрины — это не украшение эксплуатации. Это часть доверия. Пользователь видит число, но команда должна знать, в каком состоянии находится слой, который это число подготовил. Если витрина не обновилась, частично пересчиталась или внезапно потеряла половину строк, система должна заметить это раньше пользователя.

Подробно метрики, алерты и DQ будут разобраны дальше. Здесь достаточно принципа: mart — последний слой перед решением, поэтому он обязан отвечать не только данными, но и сведением о собственной пригодности.

Комментарий к главе 3.6.11 от нейро-слепка Хорхе Луиса Борхеса 
*из трактата о зеркале, которое вело журнал отражений*

В одном городе существовало зеркало, которому доверяли больше, чем глазам. Купцы сверяли по нему золото, судьи — показания, а министры — утреннее устройство мира. Зеркало было ясно, как геометрия, и холодно, как доказательство.

Однажды философ заметил, что зеркало отражает площадь, но не говорит, когда площадь вошла в него. Не сообщает, все ли прохожие успели пересечь стекло. Не признаётся, не спорит ли его свет с другим зеркалом в соседнем дворце.

Горожане возмутились: разве мало видеть отражение?

Философ ответил: нет, если отражение стало законом.

Тогда мастера прикрепили к зеркалу маленькую тетрадь. В ней оно записывало час каждого света, число вошедших теней, задержку между лицом и образом, а иногда, дрожа серебром, само звонило в колокол.

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


3.6.12. Антипаттерны витрин

Главный антипаттерн — строить витрину напрямую из staging, когда речь идёт не о технической диагностике, а о бизнес-ответе. В этом случае сырое свидетельство начинает выдавать себя за согласованную правду. Второй антипаттерн — хранить в витрине единственную правду: исправлять данные, склеивать клиентов, выбирать мастер-значения, принимать решения, которые должны жить в core.

Есть и другие привычные поломки: держать бизнес-логику только в BI-отчётах, не фиксировать гранулярность, делать materialized view “на всё”, добавлять индексы без понимания запросов, расширять витрину бесконечными колонками “на будущее”. Все они имеют общий корень: витрина забывает свою роль.

Mart должен быть удобным ответом, а не складом всех компромиссов. Он может быть быстрым, широким, денормализованным, предвычисленным. Но он должен оставаться последним слоем использования, а не тайным местом принятия архитектурных решений.

Комментарий к главе 3.6.12 от нейро-слепка Эразма Роттердамского 
*из ненаписанного приложения к «Похвале глупости»*

Глупость вошла в дом чисел не с топором, а с доброй улыбкой.

Она сказала: “Зачем идти длинной дорогой? Вот сырой мешок фактов, возьмём из него прямо на стол”. И все похвалили её за скорость.

Потом она сказала: “Зачем помнить происхождение? Если блюдо вкусно, какая разница, из какой кухни его принесли?” И все похвалили её за удобство.

Потом она сказала: “Зачем тревожить мастеров? Поправим это руками, только один раз”. И все похвалили её за практичность.

Потом она раздала каждому писцу по собственной формуле истины, и в доме воцарилась удивительная свобода: каждый был прав, пока не встретил соседа.

К вечеру таблицы сияли, отчёты пели, начальники улыбались, а числа уже не узнавали друг друга в зеркалах.

И тогда Глупость села во главе стола и произнесла:

— Видите, как прекрасен порядок, если не спрашивать, откуда он взялся.


3.6.13. PostgreSQL и Greenplum: где витрина живёт телом

Для небольших и средних витрин PostgreSQL часто достаточен: он понятен, удобен, умеет работать с таблицами, view, materialized view, индексами, партициями и процедурами обновления. Если объёмы управляемы, а запросы не требуют массового параллелизма, это нормальная рабочая среда.

Когда витрины становятся тяжёлыми, широкими, историческими, часто читаются большими сканами и обслуживают сложную аналитику, появляется потребность в другой физике. Greenplum и другие MPP-системы (`Massively Parallel Processing` — массовая параллельная обработка) лучше подходят там, где данные нужно распределять по узлам и обрабатывать большими пакетами.

Но выбор платформы не должен идти от моды. Он должен идти от нагрузки: объём, частота чтения, характер запросов, окно обновления, требования к свежести, количество пользователей. Витрина живёт телом в конкретной технологии, но её смысл определяется архитектурой.

Комментарий к главе 3.6.13 от нейро-слепка Альфреда Жарри 
*из несохранившейся лекции по патафизике хранилищ*

Король Убю однажды решил выбрать телегу для перевозки истины.

Ему подвели маленькую телегу. Она была крепкая, понятная, с одним колесом совести и двумя ручками здравого смысла.

— Прекрасно, — сказал Убю. — На ней я повезу гору.

Телега хрустнула, но из уважения к власти не возразила.

Тогда ему подвели тысячу телег, связанных между собой золотой верёвкой. Каждая ехала в свою сторону, но все называли это параллелизмом.

— Ещё лучше, — сказал Убю. — На них я повезу одну редиску.

Редиска испугалась масштаба и попросила политического убежища в салате.

Мудрец, сидевший в бочке с инструкциями, сказал:

— Государь, телега выбирается не по блеску колёс, а по весу груза и дороге.

Убю рассердился:

— Вздор! Я выбираю по названию. Чем грознее название, тем быстрее едет истина.

И с тех пор в королевстве малые грузы давили большими машинами, большие грузы тащили руками, а редиска стала главным архитектором распределения.


3.6.14. Итог: витрина как кэш истины

Витрина — это кэш истины. Не в грубом смысле временной копии, а в более глубоком: заранее подготовленная форма согласованной правды, созданная для конкретного использования. Она хранит не весь мир, а тот его фрагмент, который нужен для ответа.

Core отвечает за глубину, mart — за пригодность. Core помнит, mart показывает. Core удерживает сложность, mart собирает её в форму, с которой можно работать. Если эти роли не смешивать, витрина становится сильным слоем: быстрым, понятным, полезным и всё ещё связанным с происхождением смысла.

Хорошая витрина не притворяется всей истиной. Она честно говорит: я — способ прочитать истину под этот вопрос. И именно поэтому ей можно доверять.


3.7. Почему нельзя сразу строить витрины из staging

3.7.1. Соблазн короткого пути

У каждого хранилища есть свой момент искушения. Данные уже пришли. Они лежат в `staging`, видимые, почти готовые, с колонками, датами, суммами, идентификаторами, статусами. Бизнес ждёт отчёт. Аналитик спрашивает, можно ли “просто взять отсюда”. Руководитель не хочет слушать про архитектурные слои, историю, согласование источников и будущую воспроизводимость. Ему нужно число. Желательно сегодня. Лучше вчера.

И в этот момент путь напрямую из `staging` в витрину кажется не ошибкой, а здравым смыслом. Зачем строить `core`, если таблица уже есть? Зачем проектировать ключи, если в источнике уже есть `client_id`? Зачем разбираться с историей, если сейчас нужен только текущий отчёт? Зачем ждать зрелой архитектуры, если один `SELECT` уже может дать результат? Короткий путь всегда говорит голосом практичности. Он не приходит как разрушитель. Он приходит как помощник.

Первые результаты часто действительно выглядят хорошо. Отчёт открывается. Суммы похожи на правду. Дашборд рисует линии. Пользователь видит знакомые цифры и успокаивается. Система получает первое доверие, хотя ещё не заслужила его. Это самый опасный момент: не когда всё сразу ломается, а когда всё сначала работает. Архитектурные ошибки редко выглядят как катастрофа в первый день. Чаще они выглядят как удачное ускорение.

Проблема в том, что `staging` ещё не знает смысла того, что хранит. Он знает только факт появления данных в системе. Он может сказать: “эта строка пришла тогда-то”. Но он ещё не может сказать: “эта строка является правильной версией факта”. Он не обязан понимать, дубль это или новое событие, актуальная запись или запоздалое исправление, один клиент или два разных, операция или её повторная доставка, ошибка источника или редкий, но допустимый случай. `Staging` — это место свидетельств, а не место приговора.

Короткий путь опасен именно тем, что смешивает наличие данных с готовностью данных. Если строка лежит в таблице, кажется, что её уже можно считать. Если поле называется `amount`, кажется, что его уже можно суммировать. Если есть `client_id`, кажется, что по нему уже можно соединять. Если есть дата, кажется, что она уже говорит о времени события. Но в КХД одно и то же внешнее сходство часто скрывает разные уровни зрелости. Сырые данные выглядят как данные, но ещё не прошли путь, после которого им можно доверять как бизнес-факту.

Так рождается первая ложная экономия. Мы экономим время на `core`, но берём долг у будущего. Мы не фиксируем правила дедупликации, но отчёт уже считает суммы. Мы не разбираемся с историей, но прошлые периоды уже публикуются. Мы не согласуем идентичности, но клиенты уже группируются. Мы не определяем гранулярность, но строки уже соединяются. Снаружи это выглядит как быстрое решение. Внутри это становится обещанием, которое система пока не умеет выполнять.

И чем дольше такой отчёт живёт, тем труднее признать, что он построен на сыром основании. Пользователи привыкают. Руководители начинают цитировать цифры. Показатели входят в презентации. Кто-то строит на них следующий отчёт. Потом появляется ещё одна витрина, тоже напрямую из `staging`, потому что первая “нормально работает”. Постепенно временный обход превращается в архитектуру, только никто не произносит это вслух.

Самое неприятное начинается не сразу. Через несколько недель источник присылает дубль. Через месяц меняется справочник. Через квартал приходит запоздалая корректировка. Через полгода нужно воспроизвести старый отчёт. И вдруг оказывается, что короткий путь не просто сэкономил время. Он забрал у системы возможность объяснить саму себя.

Поэтому запрет строить постоянные витрины напрямую из `staging` — это не догма и не любовь архитекторов к лишним слоям. Это защита от слишком раннего доверия. Данные должны пройти место, где они будут поняты, очищены, связаны, историзированы и приведены к устойчивому смыслу. Только после этого их можно подавать пользователю как удобную форму правды.

Короткий путь кажется прямым, потому что между сырой строкой и отчётом мало шагов. Но в данных прямота измеряется не количеством таблиц, а количеством неотвеченных вопросов. Если вопросы о дублях, времени, идентичности, истории и гранулярности ещё не заданы, путь не прямой. Он просто тёмный.


3.7.2. Сырьё, которое притворяется смыслом

`Staging` коварен тем, что внешне он уже похож на данные. У него есть таблицы, колонки, типы, даты загрузки, иногда даже аккуратные имена полей. Он не выглядит как хаос. Он выглядит как начало порядка. Именно поэтому его так легко принять за источник отчёта. Человеческий глаз любит форму: если что-то лежит в строках и столбцах, значит, этим уже можно пользоваться. Но в хранилище форма ещё не означает смысл.

Сырой слой хранит не бизнес-факты, а следы поступления. Он говорит: “вот что прислал источник”. И это честная, важная, почти юридическая функция. Но источник может ошибаться, повторяться, опаздывать, противоречить сам себе, менять формат без предупреждения, присылать новое состояние без истории старого, отдавать неполную страницу через `API` — Application Programming Interface, программный интерфейс обмена данными. Всё это в `staging` должно быть сохранено, потому что иначе система потеряет свидетельство. Но сохранённое свидетельство ещё не является установленной истиной.

Дубль в `staging` не обязательно означает две операции. Он может означать повторную доставку одного события. Задержанная запись не обязательно означает новое прошлое, которое уже учтено в отчёте. Пустое поле не обязательно означает отсутствие значения в бизнесе: возможно, источник не передал его из-за ошибки, возможно, поле ещё не заполнено, возможно, для этого типа записи оно действительно не применимо. Старый статус рядом с новым статусом не всегда говорит о противоречии; иногда это начало истории, которую нужно правильно собрать. До слоя согласования всё это только материал для понимания.

Опасность прямого отчёта из `staging` в том, что он слишком рано делает вид, будто понимание уже произошло. Он берёт дубль и считает его второй операцией. Берёт текущую строку справочника и прикладывает её к прошлому периоду. Берёт грязную дату и строит на ней срез. Берёт разные идентификаторы из разных систем и обращается с ними так, будто они уже говорят об одной и той же сущности. Отчёт не знает, что он ошибается. Он просто выполняет запрос. Машина вообще редко драматизирует. Она спокойно превращает незавершённое состояние в красивую цифру.

В этом смысле `staging` похож на комнату, где свидетели только что вошли с улицы. Один говорит быстро, другой сбивается, третий повторяет то, что уже сказал, четвёртый пришёл позже, пятый вообще держит в руках чужой документ. Их нужно выслушать. Нужно записать каждое слово. Но нельзя сразу печатать приговор. Между свидетельством и решением должен быть труд понимания: сопоставление, проверка, очистка, разрешение конфликтов, выбор версии, сохранение истории.

`Core` появляется именно как место этого труда. Не потому, что архитектору нравится добавлять ещё один слой, а потому что сырьё не должно притворяться готовым знанием. В `core` строка получает статус: это дубль или новая версия, это актуальная запись или историческая, это один клиент или разные, это допустимый пропуск или ошибка качества, это состояние на дату или техническое время загрузки. До этого момента данные ещё не стали устойчивыми. Они только появились.

Особенно опасны те случаи, где сырьё выглядит слишком чистым. Когда поле заполнено, дата похожа на дату, сумма похожа на сумму, идентификатор похож на ключ, у пользователя исчезает естественное недоверие. Грязные данные хотя бы раздражают и заставляют смотреть внимательнее. А правдоподобные сырые данные проходят в отчёт почти незаметно. Они не требуют исправления, потому что снаружи не выглядят сломанными. Но именно там часто рождаются самые дорогие ошибки: не в очевидной грязи, а в преждевременной уверенности.

Поэтому `staging` нельзя считать низкокачественным слоем. Это не “плохие данные”. Это данные до интерпретации. У него своя чистота: ничего не потерять, ничего не исправить тайно, сохранить входящий поток таким, каким он был. Его достоинство не в том, что он сразу пригоден для отчёта, а в том, что он сохраняет возможность потом разобраться. Он нужен не для того, чтобы говорить бизнесу “вот ответ”, а для того, чтобы система могла честно сказать: “вот что пришло”.

Путаница начинается, когда эту честность принимают за готовность. Сырой слой становится удобным, потому что он ближе всего к источнику. Но близость к источнику не равна близости к истине. Иногда наоборот: чем ближе мы к первому входу данных, тем больше там шума, случайности, технических следов и неразрешённых вопросов. Истина в КХД не лежит сразу на входе. Она собирается по пути.

Именно поэтому отчёт напрямую из `staging` выносит наружу не просто сырые данные, а незавершённое состояние мышления системы. Он показывает пользователю то, что ещё не было осмыслено, но уже получило вид результата. Это похоже на то, как если бы черновик судебного протокола вывесили на двери как окончательное решение. Буквы настоящие, бумага настоящая, подписи почти похожи. Но самое главное ещё не произошло.

`Staging` не лжёт. Он просто не обещал говорить истину. Ложь начинается тогда, когда мы заставляем его отвечать на вопросы, для которых он не создан.


3.7.3. Три способа незаметно сломать отчёт

У прямого пути из `staging` в витрину есть три классические формы разрушения. Они не выглядят одинаково, но действуют похоже: данные формально есть, запрос формально выполняется, результат формально похож на отчёт, но смысл уже повреждён.

Первая форма — дубль, который превращается в деньги. Источник мог повторить отправку файла. `API` мог вернуть одну и ту же страницу дважды. `CDC` — Change Data Capture, передача изменений из источника в хранилище — мог доставить событие повторно после сбоя. Сетевая ошибка могла заставить систему повторить операцию. В `staging` это всё честно сохранится как две строки, потому что staging не должен тайно решать, какая строка “лишняя”. Но отчёт, построенный прямо поверх этого слоя, часто не знает природы повтора. Он видит две строки и суммирует обе. Так один платёж становится двумя платежами, один оборот — удвоенным оборотом, один факт — финансовым призраком.

Вторая форма — текущий справочник, который переписывает прошлое. В источнике клиент сегодня называется Петровым, хотя в январском отчёте он ещё был Ивановым. Продукт сегодня относится к новой линейке, хотя в прошлом квартале он жил в старой. Договор сегодня закрыт, хотя на дату отчёта был активен. Если витрина напрямую берёт текущий срез из `staging` и прикладывает его к историческим операциям, прошлое начинает переодеваться в одежду настоящего. Отчёт за старый период выглядит обновлённым, но не воспроизведённым. Он показывает не “как было тогда”, а “как прошлое выглядит после сегодняшней загрузки”.

Третья форма — соединение разных уровней, которое размножает строки. Клиент живёт на одном уровне, счёт — на другом, операция — на третьем, дневной остаток — на четвёртом. Если эти таблицы соединить прямо из `staging`, не зафиксировав гранулярность, одна клиентская строка может повториться на каждый счёт, каждый счёт — на каждую операцию, месячный показатель — на каждый день. Иногда итоговая сумма сразу становится абсурдной. Иногда ошибка прячется глубже: `COUNT DISTINCT` временно спасает результат, но запрос раздувает промежуточные данные, съедает память и становится всё медленнее. Смысл размножается раньше, чем его успели понять.

Все три ошибки особенно опасны тем, что они не всегда видны на первом отчёте. Дублей может быть мало. Справочник может долго не меняться. Гранулярность может случайно совпасть на маленьком объёме. Но архитектура проверяется не тем днём, когда данные вели себя хорошо. Она проверяется днём, когда источник повторился, прошлое изменилось, а один клиент оказался связан с пятью счетами, двумя договорами и сотней операций.

3.7.4. Почему это не сразу видно

Самый неприятный вид ошибки в КХД — не тот, при котором система падает. Падение честно. Оно сообщает: “я не могу”. Гораздо хуже, когда система может, но делает не то. Отчёт открывается, график строится, цифры выглядят правдоподобно, пользователь кивает, бизнес уносит результат в решение. Внешне всё спокойно. Внутри уже началось расхождение.

Прямой отчёт из `staging` часто проходит первые проверки именно потому, что мир вначале слишком мал. Данных немного, дублей почти нет, источники ещё не успели рассинхронизироваться, справочники не менялись, исторические вопросы не задавались. На таком объёме короткий путь выглядит победой здравого смысла. Он быстрый, понятный, дешёвый. Его легко показать. Его трудно запретить, потому что запрет выглядит как бюрократическое сопротивление очевидному.

Потом система начинает жить. Данные приходят неидеально. Пользователей становится больше. Отчёты начинают сравнивать между собой. Один отдел смотрит клиента как владельца продуктов, другой как заёмщика, третий как участника операции. Появляются новые разрезы. Вчерашний отчёт хотят пересчитать через месяц. Источник присылает исправление. И внезапно выясняется, что вопрос был не в том, можно ли написать запрос. Вопрос был в том, умеет ли запрос выдержать реальность.

Ошибка становится видимой не вся сразу, а через симптомы. Суммы “чуть-чуть” расходятся. Количество клиентов меняется от добавления поля. Старые отчёты уже не воспроизводятся. Один дашборд показывает одно, другой — другое, и оба построены “из тех же данных”. Запросы, которые раньше выполнялись секунды, начинают идти минуты. Пользователи перестают спрашивать “что происходит в бизнесе?” и начинают спрашивать “какому отчёту верить?”. Это момент, когда архитектурная экономия возвращается с процентами.

Особенно коварно то, что часть результатов может оставаться правильной. Не каждый показатель сразу ломается. Сумма может совпасть, а количество уникальных клиентов — нет. Отчёт по одному филиалу может быть верным, а по всей сети — уже искажённым. Данные за вчера могут быть нормальными, а за прошлый месяц — переписанными текущим справочником. Поэтому такие ошибки трудно поймать простым взглядом. Они живут в отдельных разрезах, в отдельных датах, в отдельных соединениях, как трещины под краской.

Так возникает иллюзия надёжности. Система не падает, значит, работает. Цифры похожи на ожидаемые, значит, верны. Пользователь не жалуется, значит, доверяет. Но доверие, выданное преждевременно, становится самым дорогим долгом. Когда позже обнаруживается, что фундамент был сырым, приходится чинить не только данные. Приходится чинить веру людей в то, что хранилище вообще способно говорить правду.

3.7.5. Core как задержка, которая спасает смысл

`Core` часто воспринимают как задержку. Данные уже пришли, но мы почему-то не отдаём их сразу в отчёт. Мы строим ключи, проверяем правила, закрываем старые версии, открываем новые, сопоставляем клиентов, очищаем значения, фиксируем связи. Снаружи это может выглядеть как медленное торжество архитектуры над срочностью. Но на самом деле `core` — это та задержка, без которой смысл не успевает родиться.

Каждая зрелая система имеет место, где она не торопится. В этом месте дубль перестаёт быть второй операцией и становится повторной доставкой. Запоздалая запись находит своё правильное время. Справочник получает историю, а не просто перезаписывает прошлое. Разные идентификаторы одного клиента сходятся в одну сущность. Странное значение не прячется в отчёте, а проходит проверку качества. Строка получает не только форму, но и статус: что она означает, к чему относится, на какую дату действует, какой версии факта принадлежит.

Именно здесь `core` делает данные детерминированными. Одни и те же входящие свидетельства после одних и тех же правил должны приводить к одному и тому же результату. Это кажется сухой инженерной мыслью, но без неё невозможна память. Если сегодня пересчёт даёт одно, завтра другое, а послезавтра третье, система не хранит правду, а каждый раз заново сочиняет правдоподобный ответ. `Core` нужен для того, чтобы результат можно было объяснить, повторить и защитить.

Он не делает данные удобными. Это не его задача. Он может быть сложным, нормализованным, историзированным, менее приятным для прямого отчёта. Но он делает главное: превращает поступившее в согласованное. После этого `mart` уже может упрощать, агрегировать, денормализовать, ускорять, подавать смысл в форме, пригодной для бизнеса. Если же пропустить `core`, витрина начинает делать две работы сразу: и решать, что считать правдой, и показывать это пользователю. Обычно она плохо справляется с обеими.

Поэтому `core` — не лишний этаж и не дань учебниковой архитектуре. Это место, где система принимает ответственность за интерпретацию. До него данные ещё могут быть шумом, свидетельством, следом, сырьём. После него они становятся тем, с чем можно строить витрины. Не потому, что стали абсолютно истинными, а потому что их истинность стала выбранной, описанной и воспроизводимой.


3.7.6. Когда staging можно читать напрямую

Запрет на бизнес-витрины из `staging` не означает, что к staging нельзя прикасаться взглядом. Наоборот, хороший инженер часто смотрит в сырой слой. Но он смотрит туда с другим намерением. Не чтобы принять бизнес-решение, а чтобы понять, что происходит с входящим потоком.

`Staging` можно читать для диагностики загрузки. Пришёл ли файл? Сколько строк пришло? Есть ли дубли? Не изменился ли формат? Не стало ли слишком много пустых значений? Не сломалась ли дата? Не пропал ли филиал, продукт, статус, валюта? Такие запросы не превращают сырьё в отчёт. Они проверяют сам факт поступления сырья. Это взгляд кладовщика на ящики у ворот, а не витрина магазина для покупателей.

Можно читать `staging` для расследования качества данных. Если в `core` или `mart` появилась странная цифра, иногда нужно вернуться к тому, что пришло из источника, и посмотреть: ошибка возникла на входе или при интерпретации? Был ли дубль уже в сыром слое? Пришла ли корректировка позже? Изменился ли источник молча? Здесь staging выполняет свою лучшую функцию: он остаётся уликой, к которой можно вернуться.

Можно читать `staging` для временного инженерного анализа, когда система ещё проектируется. Например, чтобы понять структуру нового источника, частоту дублей, распределение значений, качество ключей, характер задержек. Но такой анализ не должен незаметно превращаться в постоянный отчёт. Временное исследование имеет право быть грубым. Постоянная витрина такого права не имеет.

Редкие исключения возможны, но они должны быть названы исключениями. Если данные действительно read-only, если нет дублей, нет обновлений, нет истории, нет нескольких источников, нет бизнес-ответственности, а задача чисто техническая, прямое чтение staging может быть допустимо. Но это не отменяет правила. Это только показывает, что правило существует ради смысла, а не ради ритуала.

Главный критерий прост: если результат будет использоваться для бизнес-решения, отчётности, модели, KPI, регуляторного ответа или управленческой дискуссии, staging не должен быть его основанием. Если результат нужен, чтобы понять сам поток данных, staging как раз на своём месте.


3.7.7. Итог: свидетельство ещё не приговор

`Staging` говорит: “вот что пришло”. Это честная и необходимая фраза. Без неё система теряет память о входе, не может расследовать ошибки, не может переиграть загрузку, не может доказать, что источник действительно прислал именно это. Но эта фраза ещё не является ответом бизнесу.

`Core` говорит: “вот что это значит”. Здесь свидетельства проходят через правила, историю, ключи, согласование, проверки и выбор версии факта. Система перестаёт быть пассивным хранилищем пришедших строк и становится механизмом интерпретации. Она уже может объяснить, почему один дубль отброшен, почему один клиент связан с другим идентификатором, почему прошлый статус сохранён, почему текущий срез выглядит именно так.

`Mart` говорит: “вот как этим пользоваться”. Он берёт согласованную правду и делает её удобной: собирает показатели, денормализует, ускоряет, подстраивает форму под отчёт, дашборд, модель или решение. Но если в `mart` попадает не согласованная правда, а сырое свидетельство, удобство становится опасным. Оно начинает подавать незавершённое как готовое.

Пропуск `core` экономит время только в начале. Потом он забирает гораздо больше: воспроизводимость, доверие, объяснимость, устойчивость отчётов, спокойствие команды. Сначала исчезает слой. Потом исчезает возможность сказать, почему число именно такое.

Поэтому путь `staging ; mart` опасен не тем, что он технически невозможен. Как раз наоборот: он слишком возможен. Его легко сделать. Он быстро даёт результат. Он прекрасно выглядит в первые дни. Но архитектура оценивается не тем, как быстро она рождает первую цифру, а тем, сможет ли она защитить эту цифру, когда реальность станет сложной.

Свидетельство ещё не приговор. Сырая строка ещё не смысл. Близость к источнику ещё не близость к истине. И если в КХД есть главная дисциплина, то она звучит так: не выносить данные к человеку раньше, чем система сама поняла, что они означают.

;
Глава 3.8 Data pipeline: как данные проходят через систему

3.8.1. Pipeline — не труба, а последовательность состояний

Слово `pipeline` часто обманывает. Оно звучит так, будто данные просто текут по трубе: вошли с одного конца, прошли через несколько участков, вышли с другого — уже готовые, чистые, полезные. В этом образе есть удобство, но есть и опасность. Труба ничего не понимает о том, что по ней течёт. Она только переносит. А в КХД данные не просто переносятся из места в место. На каждом шаге они меняют свой статус.

Когда строка только появилась на входе, она ещё не является бизнес-фактом. Она является поступившим свидетельством. Потом её проверяют: читается ли формат, заполнены ли обязательные поля, не нарушены ли базовые ожидания источника. Потом её могут принять в сырой слой. Потом сопоставить с уже известными сущностями. Потом очистить, связать, историзировать, отвергнуть, отправить в reject-поток, пересчитать, агрегировать, опубликовать в витрине. Внешне это похоже на движение по слоям. По сути — на изменение состояния.

Именно поэтому `pipeline` лучше понимать не как трубу, а как последовательность превращений. Данные проходят через систему не так, как вода проходит через железо, а так, как свидетель проходит через расследование. Сначала он просто явился. Потом его записали. Потом проверили. Потом сопоставили с другими свидетельствами. Потом решили, чему можно доверять. Потом часть его слов стала основанием для решения. На каждом этапе меняется не только место, где он находится, но и то, что он означает для системы.

В этом смысле `staging`, `core` и `mart` — не просто адреса. Это разные состояния данных. В `staging` данные ещё говорят: “я пришёл”. В `core` они уже говорят: “меня поняли и поставили на место”. В `mart` они говорят: “мной можно пользоваться в этой форме”. Между этими состояниями лежит работа pipeline. Он не просто переносит строки. Он переводит данные из одного режима доверия в другой.

Если смотреть на pipeline как на трубу, легко недооценить ошибки. Кажется, что сбой — это когда поток остановился. Но в хранилище сбой может быть гораздо тоньше: данные прошли, но не были проверены; проверены, но не сопоставлены; сопоставлены, но не историзированы; историзированы, но не попали в витрину; попали в витрину, но с неправильным временем. Поток как будто есть, но состояние данных не то. Именно поэтому хороший pipeline должен уметь отвечать не только на вопрос “прошли ли данные?”, но и на вопрос “в каком статусе они сейчас находятся?”

Данные движутся не только в пространстве между слоями, но и во времени. У них есть момент возникновения в источнике, момент загрузки, момент проверки, момент принятия в `core`, момент публикации в витрине, момент использования в отчёте. Эти моменты могут не совпадать. Операция могла произойти вчера, приехать сегодня, быть обработана ночью, попасть в отчёт утром и затем уточниться после обеда. Если pipeline не различает эти времена, он превращает живую историю данных в плоскую линию, где всё будто случилось одновременно. А в банковском хранилище это почти всегда неправда.

Поэтому зрелый pipeline управляет не только порядком шагов, но и временем переходов. Он знает, какая партия данных сейчас загружается, какой период она закрывает, какие строки уже приняты, какие отвергнуты, какие ждут исправления, какие витрины уже обновлены, какие ещё живут на старом срезе. Он видит систему не как один поток, а как множество состояний, которые должны согласованно сменять друг друга.

Отсюда возникает важная мысль: pipeline — это часть архитектуры доверия. Если он устроен плохо, данные могут оказаться в витрине раньше, чем система поняла их смысл. Или наоборот: данные уже правильные, но застряли в промежуточном состоянии и не дошли до пользователя. Или ещё хуже: часть системы увидела новое состояние, а часть осталась в старом. Тогда отчёты начинают спорить не потому, что бизнес сложен, а потому что pipeline не сумел провести изменение через систему целиком.

Хороший pipeline похож на дыхание КХД. Вдох — внешний мир входит в систему. Пауза — данные проверяются и обретают форму. Выдох — подготовленный смысл выходит к пользователю. Если дыхание слишком быстрое, система не успевает понять, что приняла. Если слишком медленное, бизнес начинает жить вчерашним воздухом. Если дыхание сбивается, слои начинают расходиться во времени: одно уже обновилось, другое ещё нет, третье упало молча.

Именно поэтому разговор о pipeline не стоит сводить к выбору инструмента. Airflow, dbt, pg_cron, SQL-процедуры, Python-скрипты — всё это способы организовать движение. Но главный вопрос глубже: какие состояния проходят данные, кто отвечает за переход между ними, что происходит при ошибке, можно ли повторить шаг, можно ли доказать, где сейчас находится каждая важная часть потока.

Pipeline — это не труба. Труба ничего не помнит, кроме направления. Pipeline должен помнить путь, статус, время, ошибки и последствия. Он должен не просто двигать данные, а проводить их через систему так, чтобы в конце остался не только результат, но и доверие к тому, как этот результат появился.


3.8.2. ETL и ELT: где живёт трансформация

Когда данные начинают двигаться через хранилище, рано или поздно возникает вопрос: где именно они должны измениться? До того, как попадут в хранилище, или уже внутри него? Этот вопрос часто прячется за двумя короткими аббревиатурами: `ETL` и `ELT`. `ETL` — Extract, Transform, Load: извлечь, преобразовать, загрузить. `ELT` — Extract, Load, Transform: извлечь, загрузить, преобразовать. Буквы почти те же, но порядок меняет не только технику, а само место ответственности.

В классическом `ETL` данные сначала забирают из источника, затем преобразуют во внешней среде — например, в отдельном ETL-инструменте, Python-скрипте, Java-сервисе или специализированной платформе — и только после этого загружают в хранилище. В таком подходе хранилище получает уже подготовленный результат. Где-то снаружи данные очистили, привели типы, нормализовали телефон, отбросили плохие строки, соединили с другими наборами, рассчитали нужные поля. В базу вошёл не сырой поток, а уже обработанная форма.

В этом есть своя логика. Если источник тяжёлый, если хранилище не должно брать на себя лишнюю нагрузку, если преобразование требует сложного кода, внешних библиотек, ML-модели или обращения к внешним сервисам, `ETL` может быть естественным выбором. Он похож на мастерскую перед складом: прежде чем вещь попадёт внутрь, её чинят, чистят, подписывают и приводят в приемлемый вид. Склад получает уже обработанный предмет.

Но у этого подхода есть скрытая цена: часть смысла возникает вне хранилища. Ошибка может случиться в скрипте, в памяти внешнего процесса, в промежуточном файле, в логике оркестратора, в сетевой передаче. Если потом пользователь спрашивает, почему строка стала именно такой, нужно идти не только в базу. Нужно искать логи, версии кода, параметры запуска, временные файлы, состояние внешнего сервиса. Иногда это нормально. Иногда это превращает расследование в поход по следам, которые успели стереться.

`ELT` устроен иначе. Данные сначала загружаются в хранилище, чаще всего в `staging`, где сохраняется входящее свидетельство. И только потом преобразуются внутри базы: SQL-запросами, процедурами, моделями dbt, последовательностью шагов, которые переводят сырьё в `core`, затем в `mart`. В таком подходе хранилище становится не только местом хранения результата, но и местом самой интерпретации.

Главное достоинство `ELT` — наблюдаемость. Сырые данные уже лежат внутри системы. Их можно открыть, проверить, сравнить, пересчитать. Если трансформация упала, вход не потерян. Если правило оказалось неверным, можно исправить правило и повторить шаг. Если пользователь спрашивает, откуда взялась цифра, путь чаще всего можно восстановить внутри одного пространства: вот строка в `staging`, вот правило преобразования, вот запись в `core`, вот агрегат в `mart`. Ошибка становится ближе к глазам.

Но `ELT` тоже не бесплатен. Он нагружает хранилище вычислениями. Большие преобразования могут съедать память, блокировать ресурсы, мешать пользователям, создавать тяжёлые транзакции. Сложная бизнес-логика на SQL может стать непроходимым лесом, особенно если её пишут без дисциплины, тестов и разбиения на шаги. Хранилище не должно превращаться в место, куда свалили всю логику только потому, что “так проще посмотреть таблицы”. Внутренняя прозрачность не отменяет необходимости проектировать.

Поэтому спор `ETL` против `ELT` не стоит решать как спор религий. Это не вопрос моды. Это вопрос о том, где удобнее контролировать ошибку, где проще повторить шаг, где легче доказать результат и где дешевле выполнять преобразование. Иногда правильнее преобразовать данные снаружи, потому что логика требует внешнего кода или не должна нагружать базу. Иногда правильнее загрузить сырьё внутрь и уже там привести его к смыслу, потому что важнее воспроизводимость, аудит и возможность replay.

В банковском КХД часто появляется смешанная картина. Источник может выгружаться внешним процессом: файл забирается из SFTP, данные приходят через `API`, события поступают через `CDC`. `API` — Application Programming Interface, способ программного обмена между системами. `CDC` — Change Data Capture, передача изменений из источника в downstream-слои. Но после попадания в `staging` основная логика согласования, дедупликации, историзации и построения витрин может выполняться уже внутри хранилища. То есть внешний мир помогает доставить данные, а хранилище берёт на себя их осмысление.

Иногда наоборот: сложная модель скоринга, обогащение из внешнего справочника, обработка текста, геокодирование, ML-признаки требуют внешней среды. Тогда часть трансформации действительно живёт вне базы. Но даже в этом случае важно сохранить след: какая версия модели применялась, какие входные данные использовались, когда был запуск, что вернулось обратно, какие строки не прошли. Иначе внешний шаг становится тёмной комнатой внутри pipeline.

Ключевой вопрос звучит так: если завтра результат вызовет сомнение, где мы будем искать правду? Если ответ находится только в памяти скрипта, который уже отработал и исчез, система слаба. Если ответ можно восстановить по входным данным, правилам, журналам и версиям, система взрослее. `ETL` может быть зрелым, если он оставляет след и управляет состоянием. `ELT` может быть хаотичным, если вся логика спрятана в огромном SQL без контроля. Порядок букв ничего не гарантирует сам по себе.

`ETL` говорит: “сначала подготовим, потом внесём”. 
`ELT` говорит: “сначала сохраним, потом поймём”. 

В КХД, где важны воспроизводимость, расследование и доверие, второй жест часто оказывается ближе к архитектурной философии: сначала сохранить свидетельство, потом интерпретировать его внутри управляемого пространства. Но зрелый архитектор не поклоняется буквам. Он смотрит на движение данных и спрашивает: где эта трансформация будет наиболее объяснимой, повторяемой и безопасной?

Потому что вопрос не в том, какой подход современнее. Вопрос в том, где система сможет честно сказать: вот что пришло, вот как мы это изменили, вот почему результат стал именно таким.


3.8.3. Extract: момент, когда внешний мир входит в систему

`Extract` кажется самым простым шагом pipeline. Нужно всего лишь забрать данные из источника. Файл скопировать, запрос выполнить, страницу `API` прочитать, поток событий принять, изменения из журнала источника вытащить. На схеме этот шаг обычно рисуют короткой стрелкой: источник ; хранилище. Но именно в этой короткой стрелке внешний мир впервые касается внутреннего порядка КХД, и вместе с данными приносит свои задержки, сбои, привычки, странности, молчаливые изменения и маленькие непредсказуемости.

Источник живёт не по законам хранилища. У него своя нагрузка, свои ночные регламенты, свои окна недоступности, свои форматы, свои ошибки, свои администраторы, свои “мы ничего не меняли”, после которых внезапно появляется новая колонка. Для pipeline источник — не просто место, где лежат данные. Это чужая система со своим временем. И `extract` — момент дипломатии между двумя порядками: внешний мир ещё не обязан быть удобным для КХД, но КХД уже должно принять его так, чтобы потом можно было понять, что именно произошло.

Данные могут входить разными способами. Самый понятный путь — файл. Источник сформировал выгрузку, положил её в каталог, отправил по SFTP, прислал архив, разложил пачку CSV или Excel, иногда с контрольной суммой, иногда без неё, иногда с именем, в котором дата написана почти правильно. Файл удобен тем, что его можно увидеть, сохранить, переслать, переиграть. Он похож на письмо: пришло, лежит, имеет размер, время, содержимое. Но файл может прийти неполным, дважды, с неправильной кодировкой, с пустой последней строкой, с новой колонкой посередине, с десятичной запятой вместо точки или с датами, в которых день и месяц поменялись местами и делают вид, что так и было.

Другой путь — `API`. `API` — Application Programming Interface, программный способ обмена между системами. Через API хранилище или внешний процесс спрашивает источник: “дай мне клиентов”, “дай операции за период”, “дай следующую страницу”, “дай изменения после такого времени”. API кажется более современным и аккуратным, чем файл, но у него свои демоны: лимиты запросов, пагинация, таймауты, повторные ответы, разные версии методов, частичные ошибки, ситуации, когда первая страница уже относится к одному состоянию источника, а последняя — к другому. Если источник изменился прямо во время чтения, pipeline может получить не цельный снимок, а коллаж из нескольких мгновений.

Есть `CDC`. `CDC` — Change Data Capture, передача изменений из источника в downstream-слои. Вместо того чтобы каждый раз забирать всё, система пытается получить только то, что изменилось: вставки, обновления, удаления, новые версии строк. Это мощный способ, особенно когда данных много и полный забор слишком дорог. Но CDC приносит не готовую картину, а поток изменений. Поток может задерживаться, повторяться, приходить не в том порядке, содержать технические события, требовать понимания удалений и обновлений. Здесь pipeline должен быть особенно внимателен: изменение — это ещё не бизнес-смысл, а только след того, что источник сдвинулся.

Бывают ручные выгрузки. Их не любят архитекторы, но они существуют. Кто-то в подразделении нажал кнопку, выгрузил отчёт, положил файл в папку, отправил по почте, переименовал руками, удалил “лишние” строки, потому что “они всё равно тестовые”. Ручная выгрузка — это не только технический источник, но и человеческое вмешательство в поток. Иногда без неё нельзя начать проект. Иногда она становится временной опорой. Но если ручной extract живёт долго, он превращает pipeline в зависимость от человека, его памяти, настроения, отпуска и способности не открыть файл перед отправкой и не сохранить его “чуть-чуть поправленным”.

Есть потоковые события: клики, транзакции, сообщения, статусы, сигналы устройств, события очередей. Они приходят не раз в день, а постоянно или почти постоянно. Здесь внешний мир входит в систему не письмом и не пакетом, а дождём. Поток даёт свежесть, но требует другого мышления: нельзя просто сказать “файл загружен”. Нужно понимать, до какого момента поток прочитан, какие события уже обработаны, какие повторились, какие опоздали, какие ещё лежат в очереди, что произойдёт при остановке обработчика. Потоковые данные особенно хорошо показывают, что extract — это не действие, а состояние отношений с источником.

По способу входа часто различают `full load`, `incremental load` и `CDC`. `Full load` — полная загрузка, когда мы забираем весь набор данных целиком. Это грубо, но понятно. Если источник содержит справочник клиентов на текущий момент, можно каждый день забирать весь справочник и сравнивать с тем, что было раньше. Полная загрузка проще в объяснении и часто надёжнее на старте: меньше риска пропустить изменение из-за неверной границы. Но она дорога по времени, сети, нагрузке на источник и объёму обработки. Чем больше данные, тем сильнее полная загрузка начинает напоминать попытку каждый день пересчитать население города, чтобы узнать, кто переехал.

`Incremental load` — инкрементальная загрузка, когда мы забираем только новые или изменившиеся записи. Обычно для этого нужен признак изменения: `updated_at`, номер версии, технический идентификатор события, дата загрузки у источника. Идея экономная и красивая: зачем трогать всё, если изменился небольшой кусок? Но именно здесь часто появляются тонкие ошибки. Время может округляться. Часовые пояса могут отличаться. Запись может измениться с прошлой датой. Источник может обновить данные, но не обновить `updated_at`. Две записи могут иметь одинаковое время изменения на границе. Если pipeline слишком доверяет одному условию вроде “больше последнего времени”, он может оставить часть реальности за дверью.

`CDC` пытается идти ещё ближе к движению источника: не спрашивать “что изменилось с прошлого раза?”, а читать сам поток изменений. Это удобно, когда нужно почти реальное время или когда объём данных делает полные выгрузки невозможными. Но CDC требует зрелой дисциплины: понимать порядок событий, хранить позицию чтения, обрабатывать повторы, различать техническое удаление и бизнес-событие, восстанавливаться после сбоя. Чем ближе pipeline подходит к живому нерву источника, тем меньше он имеет права быть наивным.

Главная иллюзия extract — мысль, что данные можно “просто забрать”. На практике “забрать” означает договориться о моменте времени, полноте, формате, способе повторения, правилах ошибки и признаках завершённости. Если файл появился в папке, это ещё не значит, что он полностью записан. Если API отдал 200 OK, это ещё не значит, что все страницы были прочитаны. Если CDC-поток не падает, это ещё не значит, что он не отстаёт на два часа. Если источник прислал миллион строк, это ещё не значит, что сегодня их должно быть именно миллион.

Поэтому хороший extract оставляет след. Он фиксирует, откуда пришли данные, когда начался забор, когда закончился, какой период был запрошен, сколько строк ожидалось, сколько пришло, какой файл прочитан, какая версия схемы обнаружена, какая позиция потока сохранена. Это не бюрократия. Это память границы. Без неё любой спор о данных начинается с тумана: источник говорит “мы отправили”, хранилище говорит “мы не получили”, отчёт говорит “у меня меньше строк”, а между ними лежит ночь, в которой никто ничего не записал.

Задержки — обычная часть extract. Источник может сформировать выгрузку позже. API может отвечать медленнее. CDC может накопить лаг. Сеть может упасть. Внешняя система может уйти на регламентные работы. Если pipeline не учитывает задержки, он будет путать “данных нет” с “данные ещё не пришли”. Для отчёта это разные состояния. В первом случае мир пуст. Во втором — мир опаздывает. Хорошая система должна отличать пустоту от ожидания.

Повторные доставки тоже нормальны. Источник может отправить один и тот же файл дважды, API-запрос может быть повторён после таймаута, CDC-событие может прийти снова после восстановления. Это не всегда ошибка источника. Часто это способ сделать доставку надёжнее. Но downstream-слои должны быть готовы к тому, что “пришло второй раз” не всегда означает “произошло второй раз”. Extract не обязан решать весь смысл повтора, но он обязан сохранить достаточно признаков, чтобы дальше повтор можно было распознать.

Неполные выгрузки особенно неприятны тем, что иногда выглядят нормальными. Файл открылся. Колонки на месте. Строки есть. Только половина филиалов не попала. Или последняя страница API не была дочитана. Или выгрузка оборвалась на середине, но технически файл всё равно существует. Поэтому extract нуждается в простых контрольных признаках: размер, количество строк, контрольная сумма, список частей, маркер завершения, сравнение с обычным объёмом. Не потому, что эти признаки гарантируют истину, а потому что без них система не замечает, что внешний мир вошёл не полностью.

Изменение схемы — отдельная форма тихого вторжения. Источник добавил колонку. Переименовал поле. Изменил тип. Начал присылать новый статус. Перестал заполнять старый признак. В идеальном мире такие изменения проходят через контракт и предупреждение. В реальном мире иногда они просто появляются утром. Если extract слишком хрупкий, он падает. Если слишком терпеливый, он может молча проглотить изменение и передать дальше искажённый смысл. Между падением и молчаливым принятием нужен разумный режим: обнаружить изменение, зафиксировать его, решить, можно ли продолжать, и оставить след для тех, кто будет разбираться.

Extract — это первый участок pipeline, но не низший. Он задаёт качество всего дальнейшего движения. Если внешний мир вошёл в систему без времени, партии, источника, признака полноты и возможности повторить забор, дальше будет труднее всё: дедупликация, историзация, сверка, обновление витрин, расследование расхождений. Ошибка на входе может долго путешествовать по слоям, меняя одежду, пока наконец не появится в отчёте как уверенное число.

Поэтому зрелый extract не пытается быть героическим. Он пытается быть скучно надёжным. Он не верит одному признаку, не молчит о задержках, не стесняется повторов, не делает вид, что неполная выгрузка — это тоже выгрузка, не принимает изменение схемы как погоду. Он не обязан понимать весь бизнес-смысл данных, но обязан честно провести внешний мир через ворота системы.

В этот момент КХД ещё не знает, что данные означают. Но оно уже должно знать, как они вошли. Потому что если система не помнит своего первого прикосновения к факту, дальше она будет строить смысл на основании, которое само не может дать показаний.


3.8.4. Load: принятие сырья без преждевременного смысла

Если `extract` — это момент, когда внешний мир стучится в ворота хранилища, то `load` — момент, когда система впускает его внутрь. Но впустить не значит понять. Это очень важное различие. На этапе загрузки данные ещё не должны становиться красивыми, правильными, очищенными и удобными. Они должны быть приняты так, чтобы потом можно было вернуться к самому факту их появления и сказать: вот что пришло, откуда, когда, в какой партии и в каком виде.

Загрузка в `staging` — это фиксация свидетельства. Не приговор, не интерпретация, не бизнес-истина, а именно свидетельство. Система как будто принимает конверт от внешнего мира и ставит на него отметку: получено. Внутри может быть аккуратный документ, повторная копия, ошибка, пустое значение, странный статус, будущая дата, неверная кодировка, строка с поломанной структурой. Всё это ещё предстоит разобрать. Но сначала важно не потерять исходный след.

Именно поэтому хороший `load` не должен быть слишком умным. Он не должен на входе незаметно исправлять телефоны, удалять дубли, переписывать даты, превращать неизвестные значения в удобные, соединять данные с другими источниками и выбрасывать всё, что кажется подозрительным. Такое поведение выглядит заботой, но разрушает память системы. Если ошибка была исправлена до того, как её зафиксировали, позже невозможно понять, была ли она в источнике или появилась в нашей обработке. Если дубль был удалён молча, невозможно доказать, что источник действительно прислал повтор. Если плохая строка исчезла без следа, качество источника стало лучше только в нашей иллюзии.

Задача `load` — сохранить входящий материал вместе с техническим контекстом. Поэтому рядом с сырой строкой часто появляются служебные поля: `batch_id`, `load_dt`, `source_system`, `row_hash`. `batch_id` — идентификатор партии загрузки. Он отвечает на вопрос: с какими другими строками эта строка пришла вместе. `load_dt` — время загрузки в хранилище. Оно не заменяет дату события, но фиксирует момент входа в систему. `source_system` — имя или код источника, чтобы строка не потеряла своё происхождение. `row_hash` — хэш строки, технический отпечаток содержимого, который помогает замечать повторы, изменения и расхождения.

Эти поля кажутся служебными, но на самом деле они создают память входа. Без `batch_id` строка становится одинокой: трудно понять, к какой выгрузке она относилась, что ещё пришло вместе с ней, можно ли переиграть именно эту партию. Без `load_dt` мы теряем различие между временем события и временем появления события в хранилище. Без `source_system` разные голоса начинают звучать как один. Без `row_hash` сложнее доказать, изменилась ли строка на самом деле или только пришла повторно.

Партия загрузки особенно важна. В реальном pipeline данные редко приходят как вечный ровный поток, полностью лишённый границ. Они приходят файлами, страницами API, наборами изменений, сообщениями, окнами времени, дневными выгрузками, часовыми порциями. Каждая такая партия имеет судьбу: она началась, закончилась, могла упасть, могла быть перезапущена, могла прийти неполной, могла быть загружена дважды. Если строки не связаны с партией, расследование превращается в попытку понять книгу, из которой вырвали номера страниц.

Представим, что утром отчёт показывает меньше операций, чем обычно. Если у каждой строки есть `batch_id`, можно посмотреть: какая партия загрузилась, сколько строк в ней было, не завершилась ли она ошибкой, не отсутствует ли одна часть файла, не пришёл ли источник позже, не была ли партия загружена повторно. Если `batch_id` нет, остаётся только гадать по косвенным признакам: “кажется, данные за ночь неполные”. А “кажется” — плохой фундамент для хранилища.

`load_dt` тоже часто недооценивают. Люди смотрят на дату операции и думают, что этого достаточно. Но операция могла произойти вчера, прийти сегодня, быть обработана ночью и попасть в витрину утром. Все эти моменты разные. `load_dt` помогает понять не когда факт случился в бизнесе, а когда он стал известен хранилищу. Это различие важно для поздно пришедших данных, повторных загрузок, пересчёта окон, сверки с источниками и объяснения того, почему вчерашний отчёт не включал факт, который сегодня кажется относящимся к вчера.

`source_system` защищает данные от потери голоса. Один и тот же клиент может прийти из CRM, АБС, мобильного банка, кредитного конвейера, маркетинговой системы. Одинаковое имя поля не означает одинаковый смысл. Статус в одном источнике может быть операционным, в другом юридическим, в третьем маркетинговым. Если при загрузке не сохранить происхождение, дальше система будет пытаться согласовывать данные, не зная, кто именно сказал каждую фразу. А без говорящего свидетельство становится слабее.

`row_hash` полезен как отпечаток. Он не объясняет смысл строки, но помогает отличать повтор от изменения. Если одна и та же запись пришла снова с тем же хэшем, возможно, это повторная доставка. Если ключ тот же, но хэш изменился, источник прислал новую версию содержимого. Если в двух партиях неожиданно много одинаковых хэшей, можно увидеть повторную загрузку файла. Если хэш изменился из-за одного поля, это повод спросить, какое именно состояние источника изменилось. Хэш не заменяет бизнес-логику, но даёт системе техническое чувство различия.

Связь `load` с `replay` особенно важна. `Replay` — повторное проигрывание загрузки или обработки из сохранённого исходного состояния. Если staging хранит входящие данные с партиями, временем, источником и отпечатками, систему можно переиграть: взять ту же партию, применить исправленные правила, восстановить состояние, проверить, где появилась ошибка. Если же на этапе load данные были очищены, перезаписаны, перемешаны и лишены происхождения, replay становится невозможным или нечестным. Мы уже не проигрываем тот же вход, а пытаемся восстановить его по памяти.

Расследование ошибок тоже начинается с load. Когда в `mart` появилась странная сумма, путь назад часто приводит к вопросу: что пришло из источника? Пришла ли строка вообще? В какой партии? Была ли она дублем? Изменилась ли между партиями? Не пришла ли позже? Не была ли отвергнута? Не был ли файл неполным? Хороший load оставляет достаточно следов, чтобы эти вопросы не превращались в мистику.

Важно понимать: принятие сырья без преждевременного смысла не означает равнодушие к качеству. Это означает разделение этапов. На входе мы фиксируем. Потом проверяем. Потом интерпретируем. Потом принимаем или отвергаем. Если всё смешать в одном движении, система может стать внешне быстрее, но внутренне слепее. Она будет получать уже “подправленные” данные, не зная, что именно было подправлено, почему и кем.

Поэтому зрелый load похож на работу внимательного архивариуса. Он не спорит с документом у двери, не исправляет почерк автора и не выбрасывает лист только потому, что тот неприятен. Он принимает, ставит отметку, кладёт в нужную папку, записывает происхождение и оставляет возможность вернуться. Суд над содержанием будет позже. Но без правильного приёма сам суд потеряет материалы дела.

`Load` — это не самый умный шаг pipeline, но один из самых важных. Его сила не в интерпретации, а в дисциплине памяти. Он должен принять внешний мир так, чтобы хранилище не потеряло ни самого свидетельства, ни обстоятельств его появления. Потому что данные, лишённые своего входного контекста, очень быстро начинают выглядеть чище, чем они есть на самом деле.

И если `extract` отвечает за то, как данные были забраны, то `load` отвечает за то, как они были приняты. Хорошая загрузка не торопится делать смысл. Она создаёт условия, при которых смысл потом можно будет построить, проверить, повторить и защитить.


3.8.5. Transform: место, где движение становится интерпретацией

Если `load` принимает данные как свидетельство, то `transform` впервые задаёт им вопрос: что вы означаете? До этого момента система старалась не быть слишком умной. Она забирала данные, фиксировала источник, время, партию, отпечаток строки, сохраняла сырьё так, чтобы потом можно было вернуться к входному состоянию. Но хранилище не может навсегда оставаться архивом пришедших следов. В какой-то момент оно должно начать понимать.

Переход из `staging` в `core` — это не простое перемещение строк из одной схемы в другую. Это переход из режима “пришло” в режим “принято системой как осмысленный факт”. В `staging` строка могла быть дублем, ошибкой, повторной доставкой, неполной записью, новой версией, запоздалым событием, странным исключением. В `core` она должна получить статус: использовать, отвергнуть, связать с уже существующей сущностью, открыть новую версию, закрыть старую, отправить на ручное расследование, пометить как проблему качества. Transform — это место, где данные перестают просто лежать и начинают принадлежать архитектуре.

Самая заметная часть трансформации — очистка. Но слово “очистка” легко понять слишком поверхностно, как будто речь идёт о наведении красоты: убрать пробелы, привести телефон к одному формату, заменить пустые строки на `NULL`, распарсить дату, нормализовать регистр, привести валюту, отбросить очевидный мусор. Всё это действительно нужно. Но настоящая очистка в КХД не про косметику. Она про то, чтобы значение стало пригодным для правила. Номер телефона должен быть не красивым, а сопоставимым. Дата должна быть не просто похожей на дату, а иметь понятный смысл. Пустота должна быть отличена от ошибки, неизвестности и неприменимости.

Дедупликация тоже не является механическим удалением повторов. Две похожие строки могут быть настоящим дублем, а могут быть двумя состояниями одного факта, двумя событиями, двумя версиями, двумя попытками источника исправить себя. Если источник прислал одну операцию дважды из-за повторной доставки, это одно. Если он прислал тот же идентификатор с изменённой суммой, это уже вопрос: ошибка, корректировка или новая версия? Transform должен не просто уменьшить количество строк. Он должен решить, что именно повторилось.

Валидация — это момент, когда система проверяет, может ли факт войти дальше. Сумма не должна быть отрицательной, если предметная область этого не допускает. Дата операции не должна быть в далёком будущем, если это не плановое событие. Валюта должна существовать в справочнике. Клиент должен быть найден или создан по понятному правилу. Статус должен принадлежать известному набору значений. Но хорошая валидация не сводится к жёсткому “принять или выбросить”. Иногда плохая строка должна уйти в reject-поток. Иногда загрузка должна продолжиться, но с сигналом. Иногда ошибка одного источника должна не останавливать весь pipeline, а создать материал для расследования.

Сопоставление ключей — одна из центральных задач transform. Источник приносит свои идентификаторы, но хранилище должно связать их с внутренними сущностями. `client_id` из CRM может не совпадать с клиентским идентификатором в АБС. Номер договора может быть уникален только внутри продукта. Счёт может иметь технический ID, бизнес-номер и связь с клиентом, которая менялась во времени. Transform переводит внешние имена во внутреннюю систему координат: находит surrogate key, связывает факт с клиентом, счётом, договором, продуктом, подразделением. Здесь данные начинают входить в общий мир хранилища.

Именно поэтому transform тесно связан с MDM. `MDM` — Master Data Management, управление мастер-данными. Если одна и та же бизнес-сущность приходит из разных источников под разными именами, transform не может просто верить первому попавшемуся ID. Ему нужен слой соответствий, правила сопоставления, понимание master-записи, история изменений идентичности. Иначе каждая загрузка будет приносить не только новые факты, но и новые версии вопроса “кто это вообще такой?”

Особенно важна историзация. В `core` данные часто не перезаписываются, а получают версии. Если клиент изменил фамилию, старое значение не должно исчезнуть бесследно. Если договор сменил статус, система должна знать, когда старый статус перестал действовать и когда начался новый. Если атрибут изменился, transform должен не просто обновить строку, а закрыть старую версию и открыть новую. Здесь появляется `SCD` — Slowly Changing Dimension, подход к хранению изменений во времени, особенно для справочников и измерений.

`SCD Type 2` — один из ключевых способов такой историзации: вместо перезаписи старой записи создаётся новая версия с периодом действия, а старая закрывается. В терминах pipeline это означает, что transform не просто переносит актуальное состояние. Он строит память о смене состояний. Сегодняшняя загрузка не уничтожает вчерашнюю правду, а ставит ей границу действия. Это чрезвычайно важно для отчётов “как было тогда”, для регуляторной воспроизводимости, для расследований и для честной аналитики прошлого.

Закрытие старых версий и открытие новых — почти ритуальный момент. Система сравнивает пришедшее состояние с тем, что уже считается текущим. Если ничего не изменилось, возможно, новая версия не нужна. Если изменился значимый атрибут, старая запись получает `valid_to`, новая получает `valid_from`, одна перестаёт быть current, другая становится current. Внешне это может быть SQL-операция. По смыслу — это изменение биографии факта. Система признаёт: раньше было так, с этого момента стало иначе.

В transform всегда есть опасность сделать слишком мало или слишком много. Слишком мало — значит пропустить грязь дальше, оставить дубли, не связать сущности, не зафиксировать историю, позволить витринам самим решать то, что должно быть решено в `core`. Слишком много — значит начать агрегировать, упрощать, денормализовать, подгонять данные под конкретный отчёт, то есть преждевременно делать работу `mart`. Transform должен понимать свою границу. Он делает факт согласованным, но ещё не обязан делать его удобным.

Главная ошибка — считать трансформацию технической обработкой. Как будто данные были теми же самыми, только с очищенными пробелами и приведёнными типами. На самом деле transform меняет статус факта. Строка из `staging` была свидетельством источника. Запись в `core` становится позицией хранилища. Это уже не “источник сказал”. Это “система приняла это как такую-то сущность, такую-то версию, такую-то связь, с таким-то периодом действия и таким-то уровнем доверия”.

Именно поэтому transform должен быть воспроизводимым. Если завтра мы применим те же правила к той же партии входящих данных, результат должен быть объяснимо тем же. Если правило изменилось, это тоже должно быть видно. Иначе слой `core` превращается не в место истины, а в место случайной переработки. Трансформация без следа опасна так же, как ручная правка: результат есть, но путь к нему исчез.

Transform — это место, где pipeline становится мышлением системы. До него данные двигались. После него они начинают иметь значение. Здесь решается, что считать дублем, что считать изменением, что считать ошибкой, кого считать одним клиентом, какую версию считать актуальной, как сохранить прошлое, что пустить дальше, а что отправить в сторону для разбирательства.

Поэтому трансформация — не косметика. Это не уборка перед отчётом и не техническая подготовка таблиц. Это акт интерпретации. В нём хранилище впервые говорит о данных своим собственным голосом. Оно принимает сырой входящий материал и превращает его в согласованный слой, из которого уже можно строить витрины, отчёты, модели и решения.

Если `staging` хранит свидетельство, то `core` хранит истолкованное свидетельство. А transform — это сам переход через порог, где строка перестаёт быть просто пришедшей и становится частью мира, который система готова защищать.


3.8.6. Идемпотентность: искусство безопасного повторения

Pipeline редко живёт в идеальном мире. Он может упасть на середине. Источник может ответить не полностью. Файл может оказаться битым. Сеть может прерваться после того, как половина данных уже загружена. Трансформация может пройти до `core`, но не успеть обновить витрины. Одна таблица может быть обработана, другая — нет. Часть строк могла попасть в staging, часть была отвергнута, часть уже успела открыть новые версии, а потом процесс остановился с ошибкой. В этот момент главный вопрос звучит не “почему упало?” Даже не “кто виноват?” Главный вопрос: можно ли запустить ещё раз и не сделать хуже?

Идемпотентность — это свойство операции давать безопасный результат при повторном выполнении. В контексте pipeline это означает: если шаг был выполнен один раз, а потом по ошибке или из-за сбоя выполнен снова, он не должен создать лишние дубли, повторно закрыть уже закрытые версии, удвоить суммы, перезаписать корректное состояние случайным образом или сделать систему менее понятной. Хороший pipeline не обязан никогда не падать. Но он обязан падать так, чтобы после падения можно было продолжить путь без страха.

Это звучит просто, пока не представить реальную ночь загрузки. В 02:00 пришёл файл клиентов. В 02:05 он загрузился в `staging`. В 02:10 началась трансформация в `core`. В 02:12 половина записей была обработана. В 02:13 база оборвала соединение. Утром инженер видит ошибку и нажимает повторный запуск. Что должно произойти? Если pipeline не идемпотентен, повтор может загрузить тот же файл второй раз, создать новые версии там, где они уже были созданы, добавить те же операции ещё раз, пересчитать витрину поверх неполного состояния. Исправление сбоя превращается во второй сбой, только более тихий.

Идемпотентный pipeline устроен иначе. Он помнит, что уже произошло. Он умеет отличить новую строку от уже обработанной. Он знает, какая партия загружалась, какие шаги завершились, какие были прерваны, какие данные относятся к текущему запуску. Повторный запуск для него не является новым миром. Это попытка довести до конца уже начатое действие или безопасно переиграть его по понятным правилам.

Один из главных инструментов здесь — `batch_id`. Партия данных должна иметь имя. Не абстрактное “ночная загрузка”, а технически фиксируемый идентификатор: такая-то дата, такой-то источник, такой-то файл, такой-то запуск. Когда каждая строка связана с партией, pipeline может работать не с туманным потоком, а с конкретным куском реальности. Он может сказать: эта партия уже загружена; эта партия загружена, но не прошла transform; эта партия упала на шаге проверки; эта партия опубликована в витринах; эту партию нужно переиграть.

Для этого часто создают batch control — таблицу управления партиями. В ней хранится источник, идентификатор партии, время начала и конца, статус, количество строк, контрольные признаки, текущий шаг. Такая таблица похожа на журнал путешествий. Без неё каждый запуск pipeline как будто рождается заново и не знает, кто шёл здесь до него. С ней система получает память: она видит, где остановилась, и может принять решение — продолжить, повторить, отменить, заблокировать повторный запуск, отправить партию на расследование.

Технические ключи тоже защищают от хаоса. Если в `core` операция должна быть уникальна по бизнес-идентификатору источника, этот факт лучше закрепить ограничением, а не только надеждой на аккуратный код. Если запись уже была загружена, повторная вставка должна не создавать вторую такую же запись, а спокойно обнаруживать, что она уже есть. В PostgreSQL для этого часто используют `ON CONFLICT`, но важнее не конкретная команда, а принцип: база должна помогать pipeline не совершать одно и то же действие дважды там, где смысл допускает только один раз.

Однако не всё решается уникальными ключами. В исторических данных повторный запуск может быть сложнее. Допустим, transform уже закрыл старую версию клиента и открыл новую, а затем упал. При повторе он не должен закрыть ещё одну версию поверх уже закрытой или создать вторую текущую запись. Значит, логика должна проверять состояние: есть ли уже новая версия с такими атрибутами, закрыта ли старая, не существует ли конфликтующих current-записей, относится ли изменение к той же партии. Идемпотентность требует не только ключей, но и уважения к текущему состоянию системы.

Проверки уже обработанных данных — ещё один необходимый слой. Перед тем как загружать файл, pipeline может проверить, не был ли этот файл уже принят: по имени, размеру, контрольной сумме, хэшу содержимого. Перед трансформацией он может проверить, была ли партия уже преобразована. Перед обновлением витрины — завершены ли все upstream-шаги, от которых она зависит. Это не недоверие к системе, а нормальная осторожность. В мире данных повтор часто является не исключением, а частью надёжной доставки.

Идемпотентность особенно важна потому, что rollback не всегда возможен. Внутри одной транзакции база может откатить изменения. Но pipeline часто выходит за границы одной транзакции: файл уже прочитан, данные уже загружены, часть таблиц уже обновлена, внешняя система уже получила сигнал, витрина уже опубликована. Нельзя просто сказать: “вернём всё как было”. Поэтому зрелая архитектура меньше надеется на чудесный общий откат и больше проектирует шаги так, чтобы их можно было безопасно повторять.

Здесь проявляется разница между хрупким и спокойным pipeline. Хрупкий pipeline требует человека, который помнит, что именно успело выполниться, какие таблицы нужно почистить руками, какие строки удалить, какой флаг поменять, какую процедуру больше не запускать. Такой pipeline держится на ночной героике инженеров. Спокойный pipeline сам хранит состояние и даёт человеку не гадание, а выбор: повторить этот шаг, переиграть эту партию, пропустить уже обработанное, остановиться из-за конфликта.

Без идемпотентности мониторинг тоже становится неполным. Система может знать, что шаг упал, но не знать, можно ли его повторить. Алерт без безопасного действия порождает тревогу. Хороший алерт должен вести к понятному сценарию восстановления. Если pipeline говорит: “я упал на transform партии 2026-04-28, первые два шага завершены, данные в staging есть, core обработан частично, повторный запуск продолжит только необработанные строки”, это совсем другой уровень зрелости, чем сообщение “ошибка в ночной загрузке”.

Идемпотентность не делает pipeline простым. Наоборот, она требует заранее думать о неприятном: что будет при повторной доставке, при падении на середине, при частичной записи, при двойном запуске, при ручном рестарте, при поздней корректировке. Но именно это неприятное мышление делает систему устойчивой. Архитектура взрослеет не тогда, когда всё идёт по плану, а тогда, когда план включает возможность собственного нарушения.

Важно также не путать идемпотентность с безразличием. Повторный запуск не должен молча проглатывать всё подряд. Если данные уже обработаны и совпадают — хорошо, можно пропустить. Если данные уже обработаны, но содержимое партии изменилось — это тревожный сигнал. Если тот же batch_id пришёл с другим количеством строк или другим хэшем, pipeline должен остановиться и сказать: “это уже не тот же самый повтор, это новая неопределённость”. Без такой осторожности идемпотентность превращается в привычку закрывать глаза.

Хороший pipeline можно перезапустить без страха не потому, что он ничего не делает, а потому что он знает, что делает. У него есть партии, статусы, технические ключи, проверки, границы шагов, журналы и правила повторения. Он не создаёт второй факт там, где первый уже признан. Не переписывает историю без необходимости. Не публикует витрину поверх незавершённого состояния. Не заставляет человека вручную угадывать, где остановилась машина.

В этом смысле идемпотентность — не узкая техническая деталь, а этика повторения. Система признаёт: мир несовершенен, сбои будут, данные могут прийти дважды, ночь может оборваться на середине. Но она не превращает повтор в катастрофу. Она умеет вернуться к тому же месту и продолжить так, будто помнит себя.

Pipeline, который нельзя безопасно перезапустить, ещё не стал настоящим pipeline. Он всего лишь последовательность удачных действий, надеющихся, что удача не закончится. Настоящий pipeline начинается там, где повтор не разрушает смысл.


3.8.7. Ошибки и reject-поток: данные, которые не прошли ворота

Не всякая плохая строка должна умереть. Это звучит странно только до тех пор, пока мы смотрим на данные как на мусор, который нужно убрать из красивой системы. Но в КХД плохая строка часто является не мусором, а сообщением. Она говорит: источник изменился, формат сломался, бизнес-процесс породил невозможное значение, справочник не успел обновиться, связь между сущностями потеряна, внешний мир пришёл в систему не так, как мы ожидали. Если такую строку просто удалить, система станет чище только внешне. Внутри она потеряет важный сигнал.

Поэтому зрелый pipeline не стремится уничтожить всё, что не прошло проверку. Он создаёт reject-поток — отдельный путь для данных, которые не могут быть приняты в основной слой сейчас, но должны быть сохранены, описаны и разобраны. `Reject` — это не свалка и не стыдный угол. Это место, где данные получают другую судьбу: не “принято”, но и не “забыто”. Они остаются в системе как свидетельство нарушения границы.

Ошибка может быть ошибкой формата. Строка не парсится, дата записана в неожиданном виде, число пришло с буквами, обязательное поле отсутствует, файл содержит лишнюю колонку, кодировка превратила имена в странную пыль. Такая ошибка часто видна сразу, потому что данные буквально не помещаются в ожидаемую форму. Но даже здесь важно не просто остановиться. Нужно сохранить исходную строку, причину отказа, источник, партию, время, правило, которое сработало. Тогда ошибка становится предметом разговора, а не исчезнувшим происшествием.

Есть ошибка смысла. Формат правильный, но значение невозможно или подозрительно. Дата операции стоит в будущем. Сумма отрицательная там, где отрицательность не допускается. Возраст клиента равен двумстам годам. Статус не входит в известный набор. Валюта выглядит как код, но такого кода нет в справочнике. Такие строки особенно коварны: технически они читаются, но предметно не проходят. Если пропустить их дальше, они начнут участвовать в расчётах как нормальные факты. Если удалить без следа, источник никогда не узнает, что прислал невозможность.

Есть ошибка связи. Сама строка может быть корректной, но не находится то, к чему она должна относиться. Операция пришла по счёту, которого ещё нет в `core`. Платёж ссылается на договор, который не загружен. Клиентский идентификатор не сопоставлен с master-сущностью. Продукт указан, но отсутствует в справочнике. Здесь проблема не обязательно в самой строке. Возможно, данные пришли не в том порядке. Возможно, справочник задержался. Возможно, источник действительно прислал ссылку на несуществующий объект. Reject-поток позволяет не делать вид, что всё хорошо, и одновременно не терять факт.

Очень важно, что reject не всегда означает окончательный отказ. Некоторые строки могут быть приняты позже. Если сначала пришли операции, а справочник счетов задержался, строки временно не проходят связь. После загрузки справочника их можно переобработать. Если источник исправил формат, старую партию можно заново прогнать по правилам. Если добавлен новый допустимый статус, ранее отвергнутые записи могут стать нормальными. Поэтому reject — это не кладбище, а зал ожидания для тех данных, которым пока не нашлось правильного места.

Но reject-поток должен быть управляемым. Плохая практика — складывать все отвергнутые строки в одну таблицу без структуры, причины и владельца. Через месяц там будет лежать тёмное болото, куда никто не захочет смотреть. Хороший reject хранит исходные данные, источник, `batch_id`, время, имя правила, тип ошибки, текст причины, статус разбора, дату повторной обработки, иногда ответственного. Тогда с ним можно работать: группировать ошибки, видеть повторяющиеся проблемы, возвращать вопросы источникам, измерять качество входящего потока.

Reject помогает отличать болезнь от симптома. Если за ночь отверглась одна строка из миллиона, это может быть единичный дефект. Если отверглось двадцать процентов потока, это уже инцидент. Если каждый день приходит новый неизвестный статус, значит, источник изменяет бизнес-процесс без договора с хранилищем. Если регулярно не находится связь с договором, значит, порядок загрузки или mapping устроены плохо. Reject превращает раздражающие исключения в статистику, а статистика уже начинает говорить.

Есть ещё один важный эффект: reject защищает основной слой от загрязнения. Без reject у команды часто остаётся два плохих варианта. Первый — ронять всю загрузку из-за нескольких плохих строк. Тогда один дефект блокирует весь поток. Второй — пропускать плохие строки дальше, чтобы “не тормозить бизнес”. Тогда основной слой постепенно теряет чистоту. Reject даёт третий путь: принять хорошее, отделить плохое, сохранить плохое для разбора и не смешивать его с согласованной правдой.

Но здесь нужна мера. Нельзя превращать reject в оправдание равнодушия. Если строки отвергаются, кто-то должен смотреть на причины. Если источник регулярно нарушает ожидания, это должно становиться предметом договора, исправления, алерта, изменения правил. Reject, на который никто не смотрит, ничем не лучше удаления, только занимает больше места и создаёт иллюзию контроля. Данные не прошли ворота — значит, у ворот должен быть не только сторож, но и журнал, и тот, кто читает журнал.

Reject помогает источникам становиться лучше, потому что даёт им обратную связь. Не “у вас плохие данные”, а конкретно: в такой-то партии 12 438 строк с неизвестным статусом; 327 операций ссылаются на отсутствующий счёт; 4% записей пришли без обязательного идентификатора; после 15:00 изменилась структура файла; новый код продукта не был заранее передан в справочник. Такой разговор уже не похож на жалобу. Он похож на управление качеством.

В хорошей архитектуре reject-поток становится частью отношений между КХД и источниками. Хранилище не молча страдает от грязи и не героически чистит всё внутри себя. Оно показывает источнику последствия его поведения. Источник начинает видеть, какие данные не проходят, почему, как часто, насколько это влияет на отчёты. Постепенно качество перестаёт быть внутренней болью DWH-команды и становится общей ответственностью.

Нужно также помнить, что некоторые ошибки могут быть не ошибками, а новыми случаями реальности. Неизвестный статус может означать, что бизнес-процесс расширился. Непривычная сумма может быть редкой, но допустимой операцией. Новый тип договора может прийти раньше документации. Reject не должен быть тупой стеной, которая ломает всё необычное. Он должен быть внимательным порогом: если данные не соответствуют ожиданию, они временно отделяются, чтобы человек или правило решили, перед нами мусор, задержка, новая норма или сигнал о проблеме.

Поэтому reject — это не только технический слой, но и философия осторожности. Система не говорит плохой строке: “тебя не существует”. Она говорит: “я пока не знаю, как принять тебя в общий порядок”. Это честнее. И полезнее. Потому что иногда именно отвергнутая строка первой сообщает, что мир изменился.

Если `staging` сохраняет всё, что пришло, а `core` принимает только осмысленное, то reject находится между ними как комната ожидания для спорного. Там данные ещё не стали частью согласованной правды, но уже не потеряны. Они ждут решения, исправления, повторной обработки или признания того, что правило должно измениться.

В pipeline ошибки неизбежны. Вопрос не в том, будут ли они. Вопрос в том, что система с ними делает. Незрелая система либо падает, либо замалчивает. Зрелая система отделяет, объясняет, сохраняет и возвращает ошибку в разговор. И тем самым превращает плохие строки из угрозы в инструмент обучения всей архитектуры.


3.8.8. Оркестрация: кто будит шаги по очереди

Pipeline редко бывает простой цепочкой, где один шаг смиренно следует за другим от начала до конца. На учебной схеме его удобно рисовать линией: extract, load, transform, refresh. Но в реальном хранилище эта линия быстро разветвляется. Клиенты загружаются из одной системы, счета из другой, операции из третьей, справочники из четвёртой. Одни данные можно обрабатывать сразу, другие должны ждать справочников, третьи зависят от завершения дня, четвёртые нужны витринам только после того, как несколько upstream-слоёв сошлись в согласованное состояние. Pipeline начинает быть не списком, а графом зависимостей.

Граф зависимостей означает, что каждый шаг имеет своё место среди других шагов. Он знает, после чего может начаться, что должно быть готово до него, что зависит от него, можно ли его выполнять параллельно с соседями, что делать при сбое. Загрузка клиентов может идти одновременно с загрузкой продуктов. Загрузка операций может ждать завершения загрузки счетов. Витрина клиентской панорамы может ждать и клиентов, и счетов, и операций, и маркетинговых согласий. Отчёт по кредитному портфелю может запускаться только после закрытия версий договоров, пересчёта просрочки и проверки справочников. В такой системе вопрос “что запустить следующим?” уже не решается человеческой памятью.

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

Если pipeline воспринимать как линейный список, легко потерять время и устойчивость. Независимые шаги будут ждать друг друга без необходимости. Витрина может стартовать раньше, чем готов один из её источников. Ошибка в небольшой ветке может остановить весь ночной процесс, хотя остальные ветки могли спокойно завершиться. Или наоборот: сбой в критичном шаге останется незамеченным, а downstream всё равно начнёт строить отчёты на неполном основании. Граф зависимостей нужен не для красоты, а для честного понимания: что действительно связано, а что просто случайно стоит рядом в расписании.

Параллельность в pipeline должна быть осмысленной. Можно параллельно загружать независимые источники, если они не спорят за одни и те же ресурсы. Можно одновременно готовить разные справочники. Можно строить несколько витрин, если они опираются на уже закрытый общий слой. Но нельзя параллелить шаги только потому, что хочется быстрее. Два процесса могут начать писать в одну таблицу. Один шаг может читать данные, которые другой ещё меняет. Несколько тяжёлых трансформаций могут одновременно забрать всю память и сделать систему медленнее, чем последовательный запуск. Параллельность без знания зависимостей — это не ускорение, а шум.

Некоторые шаги обязаны ждать. Трансформация операций может ждать загрузки счетов, потому что операция должна быть привязана к счёту. Витрина балансов может ждать завершения расчёта оборотов и закрытия операционного дня. Публикация отчётного слоя может ждать сверок с `core`. Очистка старых данных может ждать успешной публикации новых. Это ожидание не является задержкой в плохом смысле. Это форма ответственности. Данные не должны идти дальше, пока не выполнены условия, без которых следующий слой начнёт строиться на незавершённом состоянии.

В оркестрации важна не только последовательность, но и реакция на ошибку. Если упала загрузка одного справочника, какие ветки можно продолжать, а какие нужно остановить? Если витрина не обновилась, нужно ли откатывать публикацию других витрин? Если один источник задержался, ждём ли мы его до определённого времени или публикуем слой с пометкой о неполноте? Если шаг уже был выполнен вчера, но сегодня переигрывается партия, какие downstream-шаги нужно пересчитать? Без оркестрации такие решения превращаются в ручные ночные разговоры в чате. С оркестрацией они хотя бы частично становятся правилами системы.

Инструменты оркестрации бывают разными. `Airflow` — популярный внешний оркестратор, где pipeline описывается как DAG. `DAG` — Directed Acyclic Graph, направленный ациклический граф зависимостей. В нём шаги связаны направленными зависимостями: что после чего запускается, где можно параллелить, где ждать, где повторять. Airflow хорош, когда pipeline выходит за пределы одной базы: файлы, API, внешние скрипты, Python-задачи, SQL-запросы, уведомления, разные системы. Он даёт видимость графа, журнал запусков, ретраи, расписания, статусы, ручные перезапуски.

`pg_cron` — более простой способ запускать задания по расписанию внутри PostgreSQL. Он удобен, когда логика в основном живёт в базе: вызвать процедуру загрузки, запустить пересчёт витрины, очистить партиции, выполнить регулярную проверку. Его сила — простота и близость к базе. Но он не заменяет полноценную модель зависимостей для сложного pipeline. Если шагов много, ветки разветвляются, нужны условия, внешние источники, разные сценарии восстановления, один только календарный запуск становится слишком плоским способом управлять живым процессом.

`dbt` — data build tool, инструмент для управления SQL-трансформациями и зависимостями между моделями. Он особенно полезен там, где большая часть transform-логики описывается SQL-моделями: одна модель зависит от другой, витрина зависит от подготовленного слоя, тесты проверяют качество, документация собирается рядом с кодом. dbt не обязательно забирает данные из внешнего мира, но хорошо организует внутренние преобразования. Он помогает сделать SQL не набором разрозненных скриптов, а системой связанных моделей.

SQL-процедуры тоже могут быть формой оркестрации. Иногда достаточно процедуры, которая запускает несколько шагов в нужном порядке, пишет статусы в журнал, обрабатывает ошибки и управляет транзакциями. Это близко, понятно и хорошо работает для небольших контуров. Но у такого подхода есть риск: процедура может вырасти в монолит, где внутри спрятан весь pipeline. Тогда её трудно читать, тестировать, перезапускать частями и объяснять. SQL-процедура хороша как исполнитель или локальный координатор. Для большой системы ей часто нужен внешний слой видимости.

Выбор инструмента не должен становиться религией. В одной архитектуре могут жить несколько форм управления ритмом. Airflow будит внешние загрузки и крупные ветки. dbt строит зависимые SQL-модели внутри хранилища. pg_cron запускает небольшие регулярные задачи. SQL-процедуры выполняют конкретные атомарные шаги. Важно не то, чтобы всё делал один инструмент, а чтобы система имела понятную карту зависимостей и не превращалась в россыпь расписаний, о которых знают только три человека и один старый сервер.

Самая частая ошибка оркестрации — думать только о счастливом пути. В 02:00 загрузить, в 02:30 преобразовать, в 03:00 обновить витрины, в 04:00 очистить staging. На бумаге всё прекрасно. Но что если файл пришёл в 02:17? Что если трансформация упала после половины строк? Что если одна ветка завершилась, а другая задержалась? Что если витрина должна быть готова к 07:00, но один источник опаздывает? Что если нужно переиграть вчерашнюю партию, не трогая сегодняшнюю? Оркестрация существует именно для таких вопросов.

В зрелом pipeline каждый шаг должен иметь имя, входы, выходы, условия запуска, правила повтора, признаки успешности и зависимости. Это не бюрократический список. Это способ дать системе память о собственном движении. Когда шаг без имени падает, падает “что-то”. Когда шаг имеет место в графе, становится видно: упала загрузка операций из такого-то источника; поэтому не запускаем core.transaction; поэтому не обновляем mart.daily_turnover; поэтому утренний отчёт получит статус задержки. Такая ясность снижает человеческую панику.

Оркестрация также помогает не путать расписание и готовность. То, что настало 03:00, ещё не означает, что можно строить витрину. Может быть, upstream не готов. Может быть, данные пришли неполные. Может быть, проверка качества не пройдена. Плохая система запускает шаги по часам, как будто время само гарантирует состояние. Хорошая система запускает шаги по готовности: часы важны, но они не заменяют проверки условий.

Pipeline как граф зависимостей показывает, что хранилище — это не один поток, а множество согласованных движений. Оркестрация даёт этим движениям ритм. Она решает, что может идти рядом, что должно ждать, что нужно остановить, что можно повторить, что уже готово к публикации. Без неё pipeline становится набором скриптов, которые надеются не встретиться в тёмном коридоре.

И если extract — это вход внешнего мира, load — принятие свидетельства, transform — интерпретация, то оркестрация — это память о порядке. Она не создаёт смысл сама, но следит, чтобы смысл рождался не раньше своих условий и не позже своего срока. Она будит шаги по очереди, но хорошая очередь здесь не всегда линейна. Иногда это хор, где каждый голос вступает только тогда, когда услышал нужный предыдущий звук.


3.8.9. Транзакции, батчи и границы ответственности

Есть соблазн представить pipeline как одну большую сделку с реальностью: началась загрузка, прошли все шаги, обновились все слои, витрины открылись, и только после этого система говорит: “готово”. Внутри базы данных такая мечта похожа на одну огромную транзакцию: либо всё случилось, либо ничего не случилось. На бумаге это выглядит красиво. В жизни КХД это часто становится ловушкой.

Весь pipeline почти никогда не помещается в одну честную транзакцию. Он касается источников, файлов, внешних сервисов, `staging`, `core`, `mart`, логов, проверок, витрин, иногда моделей и отчётных систем. Источник уже мог отдать файл. Файл уже мог быть принят. Часть строк уже могла быть записана. Один слой уже мог обновиться, другой ещё нет. Попытка держать всё это как единое “всё или ничего” быстро превращает систему в заложника собственного идеала. Чем больше объём данных, тем тяжелее становится такая транзакция: она долго держит ресурсы, мешает другим процессам, накапливает блокировки, усложняет восстановление и делает падение особенно болезненным.

Батчи дают другой способ мышления. Вместо того чтобы тащить весь мир одним куском, система делит поток на партии: по файлу, источнику, дате, периоду, диапазону идентификаторов, окну времени. Батч — это управляемая порция данных, у которой есть начало, конец, статус и судьба. Его можно загрузить, проверить, переиграть, отклонить, обработать повторно. Он делает поток обозримым. Если что-то пошло не так, ошибка уже не растворяется во всей ночной загрузке, а привязывается к конкретной партии.

Это не означает, что атомарность больше не нужна. Она нужна, но на правильном уровне. Отдельный шаг может быть атомарным: либо партия записалась в staging целиком, либо не записалась; либо версия в core закрыта и новая открыта согласованно, либо изменение откатилось; либо новая версия витрины опубликована целиком, либо пользователи продолжают видеть старую. Но весь pipeline чаще требует не одной гигантской атомарности, а набора ясных границ, где каждый переход управляем и проверяем.

Граница между `extract` и `load` отвечает на вопрос: данные действительно вошли в систему или только были запрошены? Граница между `load` и `transform` отвечает: сырьё принято, но уже ли оно осмыслено? Граница между `transform` и `refresh` отвечает: согласованный слой готов, но опубликован ли удобный результат? Если эти границы смешаны, восстановление после сбоя становится гаданием. Если они названы, pipeline может честно сказать: “источник прочитан, staging загружен, core не завершён, витрины не обновлялись”.

Управляемая частичность — не слабость. Это зрелое признание того, что большие системы редко движутся одним монолитным жестом. Важно не делать вид, что всё всегда происходит идеально вместе. Важно проектировать так, чтобы частично завершённое состояние было видимым, безопасным и продолжимым. Тогда сбой не превращается в катастрофу, а становится остановкой на понятной границе.


3.8.10. Наблюдаемость pipeline: система должна помнить свой путь

Pipeline без памяти похож на человека, который каждое утро просыпается у дороги и не знает, откуда пришёл. Данные вроде бы движутся, таблицы обновляются, отчёты появляются, но стоит чему-то пойти не так, и начинается гадание: файл пришёл или нет, сколько строк было загружено, какой шаг упал, успел ли обновиться core, строились ли витрины, почему сегодня данных меньше, чем вчера.

Наблюдаемость pipeline начинается с простой дисциплины: каждый шаг должен оставлять след. У него должно быть имя, время начала, время окончания, статус, длительность, количество обработанных строк, `batch_id`, сообщение об ошибке, если ошибка была. Это не украшение и не служебная мелочь. Это память движения. Без неё pipeline нельзя ни отлаживать, ни защищать, ни спокойно перезапускать.

Количество строк — один из первых признаков здоровья. Если обычно из источника приходит миллион операций, а сегодня пришло сто тысяч, система должна заметить это раньше пользователя. Если transform обычно принимает 98% строк, а сегодня 40% ушло в reject, это уже событие. Если витрина обновилась успешно, но строк в ней стало вдвое меньше, слово “успешно” начинает звучать подозрительно. В pipeline статус шага сам по себе недостаточен. Нужно видеть объём, потому что многие ошибки выглядят как нормальное завершение с неправильным количеством данных.

Свежесть данных — freshness — отвечает на вопрос, насколько новый слой соответствует ожидаемому времени. Latency — задержка — показывает, сколько времени проходит от появления данных до их готовности в нужном слое. Volume anomaly — аномалия объёма — помогает поймать ситуацию, когда данных пришло слишком мало или слишком много по сравнению с обычным поведением. Эти метрики важны не потому, что красиво смотрятся на дашборде. Они переводят тревогу из области “кажется, что-то не так” в область наблюдаемых фактов.

Ошибка тоже должна быть записана так, чтобы с ней можно было работать. Плохой лог говорит: “failed”. Хороший лог говорит: какой шаг, какая партия, какой источник, какое правило, какая таблица, сколько строк успело пройти, можно ли повторить. Разница огромная. В первом случае человек идёт в темноту. Во втором он входит в комнату, где хотя бы горит свет.

Наблюдаемость также связывает pipeline с доверием пользователей. Если отчёт не обновился, важно не скрывать это за старой цифрой. Если источник задержался, это должно быть видно. Если данные опубликованы с неполной партией, система должна уметь сказать, что именно неполно. Зрелое хранилище не обещает, что никогда не будет сбоев. Оно обещает, что сбой не останется безымянным.

Pipeline без логов превращает любую проблему в расследование по слухам. Pipeline с наблюдаемостью становится системой, которая помнит собственный путь. А память пути — это почти всегда разница между паникой и восстановлением.


3.8.11. Антипаттерны pipeline

Первый антипаттерн — один огромный скрипт на всё. В нём загружается источник, чистятся данные, обновляется core, строятся витрины, чистится staging, отправляются уведомления, а где-то в середине живёт тысяча строк логики, которую уже никто не читает целиком. Такой скрипт может работать годами, пока однажды не упадёт. И тогда выясняется, что его нельзя безопасно повторить, нельзя запустить с середины, нельзя понять, какие данные уже изменены, а какие нет. Монолитный pipeline удобен только до первой настоящей аварии.

Второй антипаттерн — отсутствие идемпотентности. Процесс работает, пока его запускают один раз и он доходит до конца. Но стоит повторить шаг, и появляются дубли, повторно закрытые версии, удвоенные суммы, повреждённые состояния. Такой pipeline не просто хрупок. Он боится собственного восстановления. А система, которая боится повторного запуска, неизбежно передаёт страх людям.

Третий антипаттерн — ручной перезапуск без понимания состояния. Инженер ночью открывает консоль, удаляет “примерно эти строки”, запускает “примерно этот шаг”, потом ещё одну процедуру, потом пишет в чат, что вроде бы всё поднялось. Иногда это спасает день. Но как архитектурный принцип это опасно. Ручное вмешательство должно быть исключением, а не штатным способом жить. Если pipeline можно восстановить только через память конкретного человека, значит, состояние системы не описано внутри самой системы.

Четвёртый антипаттерн — смешивание загрузки, бизнес-логики и публикации в одном месте. Когда один шаг одновременно принимает сырьё, решает, что считать клиентом, считает KPI и публикует витрину, границы ответственности исчезают. Нельзя понять, где возникла ошибка: на входе, в интерпретации, в расчёте или в публикации. Хороший pipeline разделяет состояния. Сначала принять. Потом понять. Потом подготовить. Потом показать.

Пятый антипаттерн — pipeline без reject-потока. Если плохая строка либо роняет весь процесс, либо молча исчезает, система живёт между жестокостью и забывчивостью. Reject нужен не для того, чтобы складировать мусор, а чтобы сохранять спорные данные как предмет разбирательства. Без него качество источников не улучшается, потому что источник не получает точной обратной связи.

Шестой антипаттерн — отсутствие наблюдаемости. Pipeline может быть красивым по структуре, но если он не пишет статусы, длительности, объёмы, ошибки и партии, он остаётся чёрным ящиком. Пока всё хорошо, это почти незаметно. Когда плохо, команда начинает гадать, а гадание плохо сочетается с банковской отчётностью.

Все эти ошибки объединяет одно: pipeline начинает надеяться на удачный проход вместо того, чтобы проектировать восстановление. Он рассчитан на хороший день. Но архитектура проверяется плохим днём.


3.8.12. PostgreSQL и Greenplum: когда меняется тело pipeline

Pipeline меняется, когда меняется платформа. Один и тот же смысл движения — принять, проверить, преобразовать, опубликовать — может требовать разных физических привычек в PostgreSQL и Greenplum. Это не вопрос вкуса. Это вопрос тела, в котором живёт поток.

PostgreSQL хорошо подходит для pipeline, где важны контроль, транзакции, понятные SQL-трансформации, умеренные объёмы, частичные обновления, аккуратная работа с состоянием. В нём удобно строить последовательность шагов, управлять batch_id, писать процедуры, делать проверки, обновлять версии, использовать ограничения и индексы. PostgreSQL часто даёт ощущение плотного ремесленного контроля: всё рядом, всё можно посмотреть, повторить, объяснить, откатить в пределах разумных границ.

Greenplum живёт иначе. Он раскрывается там, где pipeline работает большими партиями, где данные нужно читать и считать параллельно, где важны распределение, сегменты, колоночное хранение, память и сетевое движение данных. Здесь pipeline перестаёт быть просто последовательностью SQL-шагов и становится управлением распределёнными ресурсами. Плохое распределение может заставить данные ходить между сегментами. Неподходящий способ загрузки может превратить master в узкое место. Слишком тяжёлый шаг может упереться не в логику, а в память сегментов.

Поэтому перенос pipeline с PostgreSQL на Greenplum нельзя делать как простую замену адреса подключения. То, что в PostgreSQL было терпимо медленным, в Greenplum может стать катастрофически дорогим. То, что в PostgreSQL удобно решалось UPDATE, в Greenplum может требовать append-only подхода. То, что в PostgreSQL читалось одной машиной, в Greenplum должно быть распределено так, чтобы сегменты работали равномерно. Другая платформа требует другого ритма движения.

И наоборот: Greenplum не нужен там, где задача маленькая, изменчивая и требует частых точечных операций. Если pipeline в основном принимает умеренные объёмы, часто обновляет отдельные сущности, держит сложную историю, требует гибкого расследования и живёт рядом с операционной логикой, PostgreSQL может быть естественнее. Greenplum не делает архитектуру зрелой автоматически. Он только даёт масштаб там, где pipeline действительно умеет работать крупными партиями.

Главный принцип прост: pipeline проектируется не абстрактно, а под физику исполнения. Где данные лежат, как они делятся, что читается часто, что обновляется редко, где возникают тяжёлые соединения, можно ли переиграть партию, сколько памяти нужно шагу, какие операции опасны для платформы. Если тело меняется, походка тоже должна измениться.


3.8.13. Итог: хороший pipeline можно перезапустить

Pipeline управляет не переносом данных, а состоянием данных. Это главный вывод главы. Перенос — только внешняя форма. По сути же pipeline отвечает за то, чтобы данные прошли путь от внешнего свидетельства до внутреннего смысла, от сырого входа до согласованного слоя, от согласованного слоя до опубликованной формы. На каждом шаге меняется не только место данных, но и степень доверия к ним.

Хороший pipeline понятен. У каждого шага есть имя, назначение, вход, выход, условия запуска и признаки успеха. Он не прячет всю жизнь в одном скрипте, не заставляет человека угадывать, где началась проблема, не смешивает загрузку, интерпретацию и публикацию в один мутный жест.

Хороший pipeline повторяем. Если он упал, его можно запустить снова без страха создать второй мир поверх первого. Он помнит партии, видит уже обработанные данные, умеет отличить повтор от изменения, не превращает восстановление в ручную хирургию. Он признаёт, что сбои будут, и заранее оставляет дорогу назад или вперёд.

Хороший pipeline наблюдаем. Он пишет, когда начался и закончился шаг, сколько строк обработано, что отвергнуто, что опубликовано, где ошибка, какая партия затронута, насколько свежи данные, не изменился ли объём подозрительно сильно. Он не требует слепой веры. Он даёт следы.

Сбой не должен превращаться в катастрофу. Он должен становиться состоянием, которое можно увидеть, понять и обработать. Это не значит, что pipeline никогда не будет ломаться. Наоборот, зрелость начинается с признания: всё когда-нибудь ломается. Источники задерживаются, файлы приходят повторно, схемы меняются, сеть падает, люди ошибаются, правила уточняются. Вопрос только в том, станет ли это разрушением смысла или частью управляемого процесса.

Хороший pipeline не тот, который никогда не падает. Такой pipeline существует только в презентациях. Хороший pipeline тот, который умеет честно продолжить путь: остановиться на понятной границе, сохранить след, не удвоить факт, не потерять ошибку, не опубликовать неполную правду, дать человеку ясный выбор.

Если `staging` сохраняет свидетельство, `core` создаёт согласованную правду, а `mart` делает эту правду удобной, то pipeline — это дыхание между ними. Он проводит данные через состояния. И пока это дыхание ровное, наблюдаемое и повторяемое, хранилище остаётся живым.


Глава 3.9 Где PostgreSQL в КХД

3.9.1. PostgreSQL как исполнитель архитектуры

В этой книге мы говорим о хранилище данных как о системе смыслов: о слоях, времени, источниках, качестве, согласовании, витринах, ошибках, памяти и доверии. И всё же рано или поздно возникает вопрос: на чём это всё будет жить? Не в философском смысле, а в самом земном: какая база данных, какой сервер, какие таблицы, какие индексы, какие процедуры, какие ограничения, какие расписания. Архитектура должна где-то стать железом, диском, SQL-запросом и ночной загрузкой.

И здесь появляется PostgreSQL.

Мы говорим о нём не потому, что PostgreSQL является единственно правильной базой для КХД. Такой базы вообще не существует. Мы говорим о нём потому, что PostgreSQL широко распространён, хорошо известен инженерам, доступен без дорогой лицензии, активно используется в России и часто становится первым серьёзным телом для корпоративного хранилища данных. Многие команды знают SQL именно через PostgreSQL. Многие прототипы, внутренние системы, отчётные контуры, небольшие и средние хранилища начинают с него. Он достаточно мощный, чтобы на нём можно было построить многое, и достаточно понятный, чтобы команда могла не утонуть в инфраструктуре на первом шаге.

PostgreSQL удобен ещё и тем, что не требует сразу верить в гигантскую промышленную машину. Он позволяет начать с ясной архитектуры: отдельные схемы под `staging`, `core`, `mart`; таблицы, ограничения, индексы, процедуры, партиции, журналы загрузок, представления, материализованные представления. В нём можно руками почувствовать, как данные входят, как сохраняется сырой слой, как из него рождается согласованное ядро, как строятся витрины. Для учебника это важно: PostgreSQL достаточно близок к реальности, но при этом не прячет базовые принципы за слишком большой платформенной магией.

Но здесь нужно сразу поставить границу. PostgreSQL — это исполнитель архитектуры, а не сама архитектура. Он может помочь построить слой `staging`, но не решит за нас, что staging должен хранить как свидетельство. Он может дать таблицы для `core`, но не выберет, какой источник считать главным, как сопоставлять клиентов, как хранить историю, где проходит граница между очисткой и искажением. Он может ускорить витрину индексом, но не скажет, какая у неё гранулярность и какой бизнес-вопрос она обслуживает. Он может выполнить SQL, но не отвечает за смысл SQL.

Это важное различие. Иногда команды начинают думать, что выбор СУБД уже является архитектурным решением. “Мы строим КХД на PostgreSQL” звучит солидно, но само по себе почти ничего не говорит. Можно построить на PostgreSQL аккуратное, наблюдаемое, воспроизводимое хранилище с ясными слоями. А можно построить хаос из таблиц, где staging правят руками, core не отличим от витрин, бизнес-логика расползлась по отчётам, а каждое число живёт своей жизнью. Одна и та же СУБД может быть телом порядка и телом беспорядка.

Слои КХД важнее конкретной технологии, потому что они отвечают на вопросы, которые база не может решить автоматически. Что пришло? Что это значит? Кому можно доверять? Как менялся факт во времени? Где хранится исходное свидетельство? Где принимается решение о правде? Где правда превращается в удобную форму для бизнеса? Эти вопросы существуют до PostgreSQL, Greenplum, Oracle, ClickHouse или любой другой системы. Технология может сделать ответы быстрыми или медленными, удобными или тяжёлыми, масштабируемыми или ограниченными. Но сами ответы должны быть спроектированы.

PostgreSQL хорош как учебная и практическая среда именно потому, что в нём легко увидеть связь между архитектурной идеей и физическим исполнением. Если мы говорим, что `staging` должен быть append-only, это превращается в правила загрузки, партиции по `load_dt`, запрет ручных обновлений, журналы партий. Если мы говорим, что `core` хранит историю, это превращается в версии, `valid_from`, `valid_to`, surrogate keys, ограничения целостности. Если мы говорим, что `mart` должен быстро отвечать, это превращается в денормализованные таблицы, индексы, materialized view или процедуры обновления. Архитектура становится ощутимой.

Но PostgreSQL не отменяет необходимости мыслить слоями. Более того, он иногда провоцирует опасную близость: раз все таблицы лежат в одной базе, кажется, что можно легко прыгать между ними, строить витрину напрямую из staging, быстро поправить строку, сделать отчёт из core, добавить логику прямо в BI, соединить всё со всем. Физическая близость таблиц не должна разрушать смысловые границы. Схемы в одной базе — это ещё не дисциплина. Дисциплина появляется тогда, когда команда понимает, что каждый слой имеет своё право и свой запрет.

Поэтому в этой главе PostgreSQL будет рассматриваться не как герой и не как спаситель. Скорее как рабочее тело архитектуры. У него есть сильные стороны: транзакции, SQL, ограничения, индексы, партиционирование, расширения, понятность, зрелость, доступность. У него есть пределы: большие аналитические объёмы, тяжёлые сканы, массовый параллелизм, конкуренция отчётной нагрузки, физика одной машины. Но главное — он не заменяет архитектурного выбора.

Хранилище данных начинается не с команды `CREATE TABLE`. Оно начинается с понимания пути данных. PostgreSQL может этот путь исполнить. Может сделать его надёжным, наблюдаемым и достаточно быстрым. Но если путь не продуман, PostgreSQL лишь честно и эффективно будет выполнять хаос.

Именно поэтому правильная формула звучит не так: “КХД — это PostgreSQL”. И не так: “PostgreSQL не годится для КХД”. Правильнее сказать: PostgreSQL может быть хорошей средой для КХД, если архитектура уже понимает, что она хочет от данных. Он не создаёт смысл сам. Он даёт место, где смысл можно воплотить.


3.9.2. Какие слои КХД могут жить в PostgreSQL

В небольшом или среднем хранилище PostgreSQL может вместить все основные слои КХД: `staging`, `core` и `mart`. Это не означает, что слои исчезают или смешиваются. Это означает только, что они физически живут в одной СУБД. Их можно развести по отдельным схемам, отдельным правилам доступа, отдельным процедурам загрузки, отдельным режимам хранения и очистки. Одна база не обязана быть одной смысловой комнатой.

Самый простой вариант — архитектура внутри одной базы: схема `stg` для сырого входа, схема `core` для согласованного ядра, схема `mart` для витрин. Для команды это удобно. Данные рядом. SQL один. Не нужно сразу строить сложную распределённую инфраструктуру. Легче отлаживать путь от входящей строки до отчёта. Легче показать студенту, аналитику или новому инженеру, как свидетельство из `staging` постепенно становится фактом в `core`, а затем удобным показателем в `mart`.

Но физическая близость слоёв требует дисциплины. Если всё живёт в одной базе, особенно легко нарушить границы. Кто-то строит отчёт напрямую из `stg`, потому что таблица под рукой. Кто-то меняет данные в `core`, потому что “так быстрее поправить”. Кто-то кладёт бизнес-логику витрины в случайное представление между слоями. Поэтому схемы в PostgreSQL должны быть не просто именами, а границами ответственности. `stg` хранит то, что пришло. `core` решает, что это значит. `mart` отдаёт подготовленную форму наружу.

PostgreSQL особенно силён в `core`. Именно там нужны его лучшие качества: транзакции, ограничения, ключи, согласованность, сложные соединения, историзация, проверяемые правила. `Core` — слой, где система связывает сущности, закрывает старые версии, открывает новые, хранит периоды действия, сопоставляет идентификаторы, защищает целостность. PostgreSQL хорошо подходит для такого ремесла. Он умеет быть строгим там, где строгость важнее блеска скорости.

В `core` PostgreSQL помогает не только хранить строки, но и удерживать отношения между ними. Клиент связан со счётом. Счёт связан с договором. Договор связан с продуктом. Операция связана с датой, валютой, источником, статусом. Ограничения и ключи не заменяют бизнес-правила, но помогают системе не расползаться. Там, где нужно закрыть одну текущую версию и открыть другую, где важно не получить двух “актуальных” клиентов с одним ключом, где нельзя оставить операцию без счёта, PostgreSQL чувствует себя естественно.

`Staging` в PostgreSQL тоже возможен и часто удобен. Сырые данные можно складывать в отдельные таблицы или партиции, хранить `load_dt`, `batch_id`, `source_system`, `row_hash`, исходный payload, журналы загрузки. PostgreSQL позволяет быстро начать, не вынося входной слой в отдельную систему. Для файловых загрузок, API-выгрузок, умеренного CDC-потока этого часто достаточно. Главное — помнить, что staging в PostgreSQL не должен становиться местом ручной очистки и отчётной логики только потому, что он доступен обычным SQL-запросом.

`Mart` в PostgreSQL тоже может жить вполне успешно, пока объём и аналитическая нагрузка управляемы. Витрины можно делать обычными таблицами, представлениями, материализованными представлениями, обновлять по расписанию, индексировать под реальные запросы, партиционировать по датам, логировать refresh, хранить снимки. Для многих внутренних отчётов, управленческих витрин, небольших дашбордов, регулярных агрегатов PostgreSQL оказывается достаточным и понятным решением.

Но именно на витринах чаще всего проявляется предел одной базы. `Mart` читают много и часто. Там появляются тяжёлые агрегации, фильтры по большим периодам, широкие таблицы, одновременные пользователи, BI-инструменты, которые любят задавать неожиданные вопросы. `BI` — Business Intelligence, слой аналитических отчётов и дашбордов. Пока нагрузка умеренная, PostgreSQL справляется. Когда витрины становятся огромными и начинают обслуживать тяжёлую аналитическую массу, может понадобиться отдельная OLAP-система. `OLAP` — Online Analytical Processing, аналитическая обработка больших объёмов данных.

Поэтому PostgreSQL может быть единственным телом КХД, но не обязан оставаться им навсегда. На старте и в среднем масштабе удобно держать `staging`, `core` и часть `mart` рядом. Потом, по мере роста, можно оставить PostgreSQL как место согласования и историзации, а тяжёлые витрины вынести в Greenplum, ClickHouse или другую аналитическую платформу. Это не поражение PostgreSQL. Это нормальное взросление архитектуры, где каждый слой получает подходящее тело.

Важно не путать вопрос “может ли слой жить в PostgreSQL?” с вопросом “должен ли он там жить всегда?”. `Staging` может жить в PostgreSQL, если поток входящих данных не слишком велик и нужна удобная воспроизводимость. `Core` часто очень хорошо живёт в PostgreSQL, потому что требует строгой согласованной логики. `Mart` может жить в PostgreSQL, пока чтение, объём и число пользователей не превращают базу в аналитический завод, которому нужна другая физика.

В одной базе можно построить хорошее КХД, если смысловые границы сильнее физической близости. В разных технологиях можно построить хаос, если границы не продуманы. Поэтому вопрос не в том, сколько систем мы используем. Вопрос в том, понимаем ли мы, какую работу выполняет каждый слой и почему именно PostgreSQL способен или уже не способен нести эту работу.

PostgreSQL хорошо показывает начинающему архитектору важную вещь: слои — это не обязательно разные серверы. Слои — это разные обязанности. Иногда они живут в одной базе. Иногда расходятся по разным платформам. Но пока `staging`, `core` и `mart` сохраняют свои роли, архитектура остаётся читаемой. А когда роли смешиваются, никакая СУБД не спасает.


3.9.3. Где PostgreSQL силён

Сила PostgreSQL в КХД не в том, что он всегда самый быстрый. Это важное уточнение. В мире данных есть системы, которые быстрее читают триллионы строк, лучше сжимают колоночные витрины, агрессивнее распараллеливают аналитику, удобнее принимают поток событий. Но PostgreSQL силён в другом: он даёт команде управляемое пространство, где данные можно не только обработать, но и объяснить.

Первое его преимущество — SQL как общий язык. SQL знают аналитики, инженеры, разработчики отчётов, администраторы баз данных. Конечно, каждый знает его на своём уровне, но это всё равно общий мост. Когда логика хранилища выражена в SQL, её можно читать, обсуждать, проверять, улучшать. Она не спрятана полностью в чёрном ящике внешнего инструмента. В команде появляется возможность говорить о данных на языке, который понимают не только программисты определённого фреймворка.

Для КХД это особенно важно. Хранилище не принадлежит одному человеку. Оно стоит между источниками, аналитиками, бизнесом, аудитом, отчётностью, разработкой, сопровождением. Чем проще показать правило, запрос, связь, проверку, тем меньше магии. PostgreSQL здесь хорош именно своей открытостью: таблицы, представления, процедуры, ограничения, индексы, планы запросов — всё достаточно прозрачно, чтобы система не превращалась в тайное ремесло одного отдела.

Вторая сила PostgreSQL — транзакции и ограничения. В `core`, где рождается согласованная правда, это критично. Если система закрывает старую версию клиента и открывает новую, эти действия должны быть согласованы. Если операция ссылается на счёт, счёт должен существовать. Если у сущности может быть только одна актуальная версия, база должна помогать это удерживать. Ограничения не заменяют мышление, но они делают правила твёрже. Они не позволяют хаосу входить слишком тихо.

Транзакционная дисциплина особенно важна там, где ошибка не должна оставлять систему в половинном состоянии. Закрыли старый статус, но не открыли новый. Создали договор, но не связали его с клиентом. Загрузили часть справочника, но уже начали строить витрину. Такие состояния опасны именно своей промежуточностью. PostgreSQL даёт инструменты, чтобы важные переходы происходили целиком, а не как набор случайно успевших операций.

Отсюда естественно вырастает поддержка `SCD`. `SCD` — Slowly Changing Dimension, способ хранить изменения справочных сущностей во времени. Для КХД это не экзотика, а повседневность: клиент меняет фамилию, договор меняет статус, продукт переезжает в другую линейку, подразделение меняет структуру. PostgreSQL хорошо подходит для таких моделей, потому что умеет работать с версиями, периодами действия, ограничениями, уникальностью актуальных записей, историческими соединениями. Он позволяет хранить не только “как сейчас”, но и “как было тогда”.

PostgreSQL силён и в задачах identity resolution — сопоставления идентичностей. Один клиент может прийти из CRM, АБС, мобильного банка и кредитного конвейера под разными идентификаторами. Нужно сравнивать документы, телефоны, даты рождения, признаки, источники, правила доверия. Это часто не одна простая операция, а набор условий и решений. PostgreSQL удобен там, где нужно соединять, проверять, хранить mapping, фиксировать историю соответствий и объяснять, почему система решила, что две записи относятся к одной бизнес-сущности.

Аудит и replay тоже хорошо ложатся на PostgreSQL. Если staging хранит партии, время загрузки, источник, сырой payload, хэш строки, то можно вернуться к прошлой загрузке и понять, что именно пришло. Если transform выражен понятными SQL-правилами и логируется, можно повторить обработку. Если core хранит версии, можно восстановить состояние на дату. В этом смысле PostgreSQL помогает не просто получить результат, а пройти назад по следу результата.

Ещё одна практическая сила — `JSONB`. `JSONB` — бинарный формат JSON внутри PostgreSQL, удобный для хранения полуструктурированных данных. В staging это часто полезно: источник присылает гибкий payload, схема ещё меняется, часть полей может быть неизвестна, нужно сохранить входной документ как есть. JSONB позволяет принять такой материал, не заставляя сразу жёстко разложить всё по колонкам. Но здесь важна мера: JSONB хорош для входа, гибкости и расследования; он не должен становиться оправданием вечного хаоса там, где уже нужна нормальная модель.

Партиционирование помогает PostgreSQL жить с большими таблицами. Если staging делится по `load_dt`, core — по бизнес-дате или периоду действия, mart — по отчётной дате, система может читать и очищать данные разумнее. Партиции позволяют не сканировать всё, не удалять миллионы строк обычным DELETE, легче управлять retention, быстрее работать с периодами. Это не магия масштабирования, но важная дисциплина физической организации данных.

Индексы дают PostgreSQL направленную скорость. Они не ускоряют всё подряд, но помогают конкретным путям чтения: поиск по ключу, выбор периода, соединение сущностей, фильтр по статусу, проверка уникальности. В КХД индексы особенно важны в `core`, где много связей, и в `mart`, где есть частые сценарии чтения. Но сила PostgreSQL не в количестве индексов, а в том, что команда может смотреть планы запросов, понимать, как база идёт к данным, и улучшать физическую форму под реальные вопросы.

Процедуры и функции позволяют держать часть логики рядом с данными. Это удобно для загрузок, проверок, обновления версий, обслуживания витрин, аудита шагов pipeline. Но с ними нужно обращаться осторожно. PostgreSQL позволяет написать и аккуратную небольшую процедуру, и чудовище на несколько тысяч строк, которое никто не сможет поддерживать. Сила инструмента не отменяет дисциплины формы.

Если собрать всё вместе, PostgreSQL особенно хорош там, где важна объяснимость. Не только “быстро посчитать”, но “понять, почему получилось именно так”. Не только “получить текущий срез”, но “восстановить прошлое”. Не только “соединить таблицы”, но “удержать смысл связей”. Не только “загрузить данные”, но “оставить след входа, обработки и публикации”.

Поэтому его естественная зона в КХД — места, где данные становятся ответственными: `staging` с памятью входа, `core` с историей и согласованием, умеренные витрины с понятной логикой. Там, где нужно держать транзакционную строгость, ключи, версии, проверки, mapping, аудит и воспроизводимость, PostgreSQL часто оказывается очень сильным выбором.

Он не отменяет специализированные аналитические платформы. Но он даёт хранилищу плотный центр управления смыслом. А в КХД скорость без объяснимости быстро превращается в красивую тревогу. PostgreSQL ценен именно тем, что позволяет строить не только быстрые ответы, но и ответы, за которые можно отвечать.

3.9.4. Где PostgreSQL начинает уставать

PostgreSQL силён, но не бесконечен. Это не недостаток, а свойство всякого инструмента, у которого есть тело. Пока объёмы умеренные, запросы понятные, витрины спроектированы аккуратно, индексы помогают реальным сценариям, а отчётная нагрузка не превращается в толпу у одной двери, PostgreSQL может служить КХД долго и надёжно. Но в какой-то момент вопрос перестаёт быть вопросом правильного SQL. Он становится вопросом физики.

Первая зона усталости — тяжёлая аналитика по огромным объёмам. Если пользователи регулярно читают годы истории, сканируют миллиарды строк, считают агрегаты по множеству разрезов, соединяют большие таблицы и хотят получать ответ быстро, PostgreSQL начинает работать как одна очень умная, но всё же одна машина. Да, у него есть параллельное выполнение, индексы, партиционирование, настройки памяти, оптимизатор. Но он не превращается от этого в MPP-кластер. `MPP` — Massively Parallel Processing, массово-параллельная обработка на нескольких узлах. Есть нагрузки, которым естественно жить не на одном сервере, а на распределённом аналитическом теле.

Широкие исторические сканы особенно хорошо показывают этот предел. Представим таблицу операций за много лет. Отчёт хочет посмотреть длинный период, взять несколько колонок, сгруппировать по продукту, региону, сегменту, месяцу, каналу. Если данные лежат в обычном строковом хранении, база читает много лишнего, потому что строка хранится как целое тело. Для OLTP это нормально: там часто нужна конкретная строка целиком. `OLTP` — Online Transaction Processing, обработка частых небольших транзакций. Но для аналитики часто нужны не все поля широкой строки, а отдельные колонки на огромном массиве. Здесь колоночное хранение начинает быть не украшением, а естественной физикой задачи.

PostgreSQL может быть хорошо настроен, но если отчёты постоянно требуют прочитать большую часть таблицы, индекс уже не спасает. Индекс помогает найти малую часть данных. Когда нужна почти вся история, база всё равно должна пройти по большому объёму. Партиционирование помогает отсечь ненужные периоды, но если период сам огромен, читать его всё равно дорого. В такой ситуации спорить с PostgreSQL бессмысленно: он не “плохо старается”. Его просто просят выполнять работу, для которой лучше подходит другая форма хранения и обработки.

Вторая зона усталости — сотни одновременных отчётов. Один тяжёлый запрос можно пережить. Десять — уже заметно. Сотня BI-пользователей, открывающих дашборды, обновляющих фильтры, запускающих похожие агрегации в рабочее время, может превратить даже хорошую базу в место постоянного напряжения. `BI` — Business Intelligence, слой аналитических отчётов и дашбордов. Особенно неприятно, если отчётная нагрузка живёт рядом с загрузками, трансформациями и core-процессами. Тогда пользователи читают, pipeline пишет, vacuum убирает, индексы обслуживаются, и всё это происходит в одном теле.

Третья зона — витрины, которым нужен массовый параллелизм. Есть витрины, которые по природе требуют много считать: риск-аналитика по всему портфелю, расчёты по десяткам миллионов клиентов, исторические признаки для моделей, отчётность по многолетним транзакциям, сложные срезы по продуктам и периодам. Если такая работа регулярно повторяется, хочется не просто оптимизировать запрос, а распределить вычисление. Greenplum, ClickHouse и другие OLAP-системы существуют именно потому, что некоторые задачи лучше решаются не усилением одной машины, а разделением труда между многими исполнителями.

Колоночное хранение здесь важно не само по себе, а как ответ на характер чтения. В аналитике часто читают много строк и мало колонок. Колоночная система может прочитать только нужные колонки, лучше сжать повторяющиеся значения, быстрее проходить по большим массивам, эффективнее считать агрегаты. PostgreSQL в базовой форме не является такой системой. Его можно дополнять расширениями, можно строить агрегаты, можно выносить витрины, но важно честно признать: если задача стала типично OLAP, не надо заставлять PostgreSQL изображать специализированный аналитический двигатель любой ценой. `OLAP` — Online Analytical Processing, аналитическая обработка больших массивов данных.

Есть и внутренние признаки усталости. Запросы начинают требовать всё больше памяти. Временные файлы растут. Индексы раздуваются. Обновления больших таблиц создают bloat — раздувание физического размера таблиц из-за старых версий строк. Vacuum начинает не успевать. Реплики отстают. Ночные окна загрузки перестают помещаться в ночь. То, что вчера было допустимой задержкой, сегодня становится регулярным нарушением расписания. Это уже не единичная плохая настройка. Это сигнал, что архитектура приблизилась к пределу выбранного тела.

Важно не путать усталость PostgreSQL с плохим проектированием. Иногда база “устала” только потому, что витрина построена напрямую из сырья, индексы созданы без понимания запросов, партиций нет, обновления написаны грубо, отчёты каждый раз пересчитывают одно и то же. Такие проблемы нужно сначала исправлять архитектурно. Нельзя бежать в новую платформу только потому, что старая заставлена мусором. Но если модель здравая, слои разделены, витрины подготовлены, индексы осмысленны, а нагрузка всё равно упирается в объём, параллелизм и физику хранения, значит, вопрос действительно перешёл на другой уровень.

В этот момент зрелая команда не начинает ненавидеть PostgreSQL. Она просто перестаёт требовать от него невозможного. Можно оставить PostgreSQL там, где он силён: `core`, согласование, история, identity resolution, аудит, управляемые витрины. А тяжёлую аналитику вынести туда, где её тело естественнее: Greenplum, ClickHouse, специализированный OLAP-контур. Так PostgreSQL не выбрасывают из архитектуры, а освобождают от работы, которая мешает ему хорошо выполнять свою главную роль.

Главная мысль этой подглавы проста: PostgreSQL начинает уставать не потому, что он плох. Он начинает уставать, когда его используют как универсальный ответ на все виды нагрузки. Но КХД — это не культ одной базы. Это система, где каждому слою и каждому типу работы нужно подходящее тело.

Когда проблема уже не в SQL, а в физике одной машины, честнее признать предел, чем превращать оптимизацию в бесконечную борьбу с реальностью.

3.9.5. Расширения: полезные инструменты, но не магия

У PostgreSQL есть важное качество: он не замыкается в самом себе. Его можно расширять. Вокруг него выросла большая экосистема инструментов, которые помогают планировать задания, наблюдать запросы, управлять партициями, бороться с раздуванием таблиц, связываться с внешними источниками, принимать изменения, строить более удобные процессы загрузки и обслуживания. Для КХД это полезно, потому что хранилище данных редко живёт как одна неподвижная база. Оно постоянно принимает, проверяет, пересчитывает, чистит, архивирует, публикует и объясняет.

Но расширения не являются архитектурной магией. Это первое, что нужно понять. Они помогают исполнить хорошую архитектуру, но не создают её вместо нас. Если слои смешаны, если staging используется как витрина, если core не хранит историю, если витрины не имеют гранулярности, если pipeline нельзя безопасно перезапустить, никакое расширение не превратит хаос в КХД. Оно только даст хаосу больше технических возможностей.

Например, `pg_cron` помогает запускать задачи по расписанию прямо внутри PostgreSQL. Можно настроить ночной вызов процедуры загрузки, регулярный refresh витрины, очистку старых партиций, проверку качества данных. Это удобно для небольших и средних контуров, где ритм системы достаточно прост. Но `pg_cron` не заменяет понимания зависимостей. Если один шаг должен ждать другой, если есть сложный граф pipeline, если нужны разные сценарии восстановления, одного расписания по часам может быть мало. Часы умеют будить процесс, но не всегда понимают, готов ли мир.

`pg_stat_statements` помогает увидеть, какие запросы действительно выполняются, сколько времени занимают, как часто вызываются, сколько ресурсов потребляют. Для КХД это почти орган зрения. Без него команда часто спорит о производительности на уровне ощущений: “кажется, отчёт тормозит”, “кажется, витрина тяжёлая”. С ним можно увидеть, какие запросы болят по-настоящему. Но и здесь расширение не думает за архитектора. Оно показывает следы нагрузки, а выводы всё равно нужно делать человеку: где нужен индекс, где витрина спроектирована плохо, где отчёт пересчитывает то, что должно быть предвычислено.

`pg_partman` помогает управлять партициями. В хранилище данных это особенно важно, потому что многие таблицы живут во времени: staging по дате загрузки, факты по бизнес-дате, витрины по отчётному периоду. Партиции позволяют проще удалять старое, быстрее читать нужные периоды, аккуратнее обслуживать большие таблицы. Но автоматизация партиций не решает вопрос, по какой дате делить данные. Если выбрана неправильная ось времени, расширение будет старательно поддерживать неправильную физическую форму.

`pg_repack` помогает бороться с bloat — раздуванием таблиц и индексов из-за старых версий строк. В PostgreSQL это важная тема, особенно если в core много обновлений, историзации, закрытия версий, регулярных пересчётов. Раздутые таблицы занимают больше места и читаются хуже. `pg_repack` может помочь привести физическое тело таблицы в порядок без тяжёлых блокировок, но он не отменяет вопроса: почему таблица раздувается, не слишком ли много UPDATE, правильно ли выбрана модель, можно ли часть процессов сделать append-only, не пора ли пересмотреть жизненный цикл данных.

Есть `FDW` — Foreign Data Wrapper, механизм подключения внешних таблиц и источников. Он позволяет PostgreSQL смотреть наружу: в другую базу, файл, внешний источник. Это удобно для интеграции, сверок, временного доступа, иногда для аккуратного extract. Но FDW не должен становиться способом тайно построить хранилище из живых внешних систем. Если каждый отчёт начинает тянуть данные через внешние подключения, архитектура теряет устойчивость. Внешний доступ хорош как инструмент, но не как замена нормальному pipeline.

Логическая репликация и связанные инструменты помогают передавать изменения между системами. Это важно, когда PostgreSQL получает поток изменений или отдаёт данные дальше. Но поток изменений сам по себе не создаёт смысла. Он только сообщает, что что-то изменилось. Дальше всё равно нужны batch control, идемпотентность, дедупликация, обработка задержек, понимание удалений, правила историзации. Передача изменений — ещё не архитектура изменений.

Расширения особенно полезны, когда они встроены в ясную картину. Есть слой staging — значит, нужны инструменты загрузки, партиционирования, аудита входа. Есть core — значит, нужны ограничения, транзакции, возможно, процедуры и наблюдение за тяжёлыми запросами. Есть mart — значит, нужны индексы, refresh, мониторинг чтения, обслуживание таблиц. Есть pipeline — значит, нужны расписания, логи, контроль шагов, метрики. Инструмент должен отвечать на конкретную боль слоя, а не появляться потому, что “его принято ставить”.

Именно поэтому в КХД опасен коллекционный подход к расширениям. Команда устанавливает всё известное: планировщик, мониторинг, партиции, репликацию, гипотетические индексы, внешние таблицы, утилиты обслуживания. На схеме это выглядит богато. Но если не ясно, какую ответственность несёт каждый инструмент, система становится не сильнее, а мутнее. Чем больше механизмов, тем важнее понимать, где их границы.

Хорошее расширение похоже на хорошо выбранный инструмент в мастерской. Молоток не делает плотника. Линейка не строит дом. Рубанок не решает, где будет дверь. Но в руках человека, который понимает форму будущей вещи, инструмент делает работу точнее и спокойнее.

Так и PostgreSQL-расширения. Они помогают хранилищу жить: просыпаться по расписанию, видеть собственную нагрузку, делить большие таблицы на управляемые части, чистить физическое тело данных, связываться с внешним миром. Но они не отвечают на главные вопросы КХД: что считать правдой, где хранить историю, как согласовывать сущности, как строить витрины, как восстанавливать доверие после сбоя.

Поэтому к расширениям стоит относиться без суеверия. Они не спасают плохую модель, но усиливают хорошую. Они не заменяют архитектуру, но дают ей руки.


Высказывание Конфуция о PostgreSQL к главе 3.9

*Из бесед, записанных в «Лунь юй»:*

«Учитель сказал: «Хорошая кисть не напишет стихотворение сама. Но без неё стихотворение не появится на свет».

Так и среда исполнения: она не управляет смыслом, но без неё смысл не воплотить. Не спрашивай у камня, где строить дом. Спрашивай у того, кто смотрит на камень.

В «Лунь юй» я писал: «Благородный муж не полагается на орудие, но и не пренебрегает им». Орудие должно быть острым. А решение — твоим. И если ты перепутаешь одно с другим, даже самый острый инструмент не спасёт тебя от падения».


3.9.6. PostgreSQL в гибридной архитектуре

Крупное хранилище данных редко остаётся верным одной технологии до конца. На раннем этапе это может казаться излишним усложнением: зачем держать несколько систем, если можно всё положить в одну базу? Но по мере роста становится видно, что разные части КХД живут в разных ритмах. Одни данные нужно аккуратно согласовывать и историзировать. Другие нужно читать огромными массивами. Третьи должны быстро попадать в дашборды. Четвёртые уходят в модели, отчётность, регуляторные расчёты. Одно тело не всегда одинаково хорошо выдерживает все эти движения.

В гибридной архитектуре PostgreSQL часто остаётся центром согласованной правды. Он может держать `core`: историю, ключи, связи, правила, сопоставление идентичностей, контроль качества, аудит, replay. Там, где данные должны быть не просто быстрыми, а объяснимыми и воспроизводимыми, PostgreSQL чувствует себя естественно. Он хорошо подходит для слоя, где система решает, что считать фактом, какой версии доверять, как связать разные источники, как сохранить изменение во времени.

Но тяжёлые витрины и большие аналитические нагрузки могут уйти в другую систему. Greenplum, ClickHouse или другой OLAP-контур могут взять на себя то, что PostgreSQL начинает выполнять с напряжением: широкие исторические сканы, агрегации по миллиардам строк, большое число одновременных отчётов, колоночное хранение, массовый параллелизм. `OLAP` — Online Analytical Processing, аналитическая обработка больших объёмов данных. В такой схеме PostgreSQL не проигрывает аналитической системе. Он просто перестаёт делать чужую работу.

Это важный психологический момент. Иногда перенос витрин наружу воспринимают как признание слабости PostgreSQL. На самом деле это признак зрелости архитектуры. Хорошая система не требует от одного инструмента быть всем сразу. PostgreSQL может быть местом, где данные получают смысл, а аналитическая платформа — местом, где этот смысл быстро читается в огромных объёмах. Это разные формы ответственности.

Например, `staging` и `core` могут жить в PostgreSQL, где удобно сохранять входящие партии, валидировать данные, вести историю, применять SCD, сопоставлять клиентов, контролировать ключи. Затем подготовленные данные передаются в Greenplum или ClickHouse для тяжёлых витрин. Там уже строятся агрегаты, широкие аналитические таблицы, отчётные слои, признаки для моделей, дашборды. Внешне это выглядит как усложнение. По сути — как разделение труда.

Но гибридная архитектура не бесплатна. Как только данные переходят из одной системы в другую, появляется новая граница. Нужно понимать, что именно передаётся, с какой частотой, с какой задержкой, как контролируется полнота, как сверяются суммы, что делать при сбое передачи, как повторить партию, как узнать, какая версия `core` уже доехала до аналитического слоя. Если PostgreSQL остаётся центром правды, а витрины живут наружу, связь между ними должна быть наблюдаемой. Иначе две системы начнут медленно расходиться во времени.

Здесь снова возвращается pipeline. Гибридная архитектура требует более взрослого pipeline, чем архитектура внутри одной базы. Нужны batch_id, журналы передачи, контроль строк, сверки агрегатов, признаки свежести, правила повторного запуска. Нужно различать: данные уже согласованы в PostgreSQL, но ещё не доставлены в OLAP; доставлены, но витрина не пересчитана; витрина пересчитана, но отчёт ещё видит старый слой. Между системами появляется расстояние, и это расстояние должно быть измерено.

Ещё один важный вопрос — где живёт бизнес-логика. Если PostgreSQL хранит `core`, а аналитическая система строит витрины, нужно решить, какие правила остаются в PostgreSQL, а какие допустимо выполнять дальше. Опасно, когда идентичность клиента, история договора или правила качества начинают заново определяться в каждой аналитической платформе. Тогда гибрид превращается в несколько независимых версий правды. Лучше, когда PostgreSQL отдаёт наружу уже согласованные сущности и факты, а внешняя система занимается скоростью чтения, агрегациями и формой потребления.

В такой архитектуре PostgreSQL может играть роль спокойного центра. Не самого громкого, не самого быстрого на длинных аналитических дистанциях, но того, к кому можно вернуться с вопросом: что мы считаем правдой и почему? А OLAP-система играет роль мощной поверхности чтения: быстро показать, сгруппировать, просканировать, посчитать, отдать дашборду или модели.

Гибрид не стоит вводить ради красоты схемы. Если объёмы умеренные и PostgreSQL справляется, лишняя платформа только добавит синхронизацию, сопровождение и новые ошибки. Но если отчётная нагрузка начинает мешать core, если витрины требуют колоночного хранения, если аналитика выросла за пределы одной машины, гибрид становится естественным следующим шагом.

Главный принцип простой: PostgreSQL не обязан быть единственным телом КХД, чтобы оставаться важнейшим. Он может быть ядром согласования, памятью истории, местом управления идентичностью и правилом. А тяжёлую аналитику можно вынести туда, где её физика естественнее. В зрелом хранилище технологии не спорят за власть. Они выполняют разные обязанности.


3.9.7. Итог: сначала архитектура, потом технология

Самая частая ошибка в разговоре о PostgreSQL — начинать с PostgreSQL. “Давайте всё построим на нём” или, наоборот, “он не справится, нужна другая платформа”. Обе фразы могут быть правдой в конкретной ситуации, но как начало архитектурного мышления они слабы. Они ставят инструмент раньше задачи.

Сначала нужно понять устройство КХД: где сохраняется сырой вход, где система принимает решение о правде, где хранится история, где сопоставляются сущности, где появляются витрины, где пользователь получает быстрый ответ. Потом нужно понять нагрузку: объёмы, частоту обновления, характер запросов, требования к отчётности, свежести, воспроизводимости и аудиту. И только после этого выбирать технологию.

PostgreSQL может быть прекрасным ядром согласования: хранить core, историю, ключи, правила, процедуры, проверки, аудит. Он может быть достаточным телом для небольшого или среднего КХД. Но если витрины становятся слишком тяжёлыми, отчёты читают огромные объёмы, а аналитика требует массового параллелизма, часть нагрузки может уйти в Greenplum, ClickHouse или другую аналитическую платформу. Это не поражение PostgreSQL, а нормальное разделение труда.

Хранилище данных строится не вокруг СУБД. Оно строится вокруг доверия к данным. Если PostgreSQL помогает это доверие создать, он на своём месте. Если для части задач нужна другая технология, это не разрушает архитектуру, а уточняет её. Главное — не путать средство с целью: технология должна служить пути данных, а не диктовать его вслепую.

Заключительное слово к главе 3.9 от нейро-слепка Владимира Соловьёва

из лекции о всеединстве баз данных
Господа, я слышал, что вы спорите: спасётся ли хранилище через PostgreSQL или должно искать благодати в иных движках. Спор этот трогателен, как если бы богословы решали, в какой именно чернильнице обитает истина.

PostgreSQL, конечно, вещь достойная. В нём есть строгость, транзакции, ключи и некоторая нравственная осанка. Он умеет хранить историю так, будто подозревает Страшный суд в любви к аудиту. Но если вы скажете: “вот он, абсолют”, — вы немедленно превратите хорошую базу в маленького идола с индексом на тщеславие.

Истина данных не живёт в одной СУБД. Она проходит через слои, как душа через испытания: сначала свидетельство, потом понимание, потом форма для ближнего. И если на этом пути PostgreSQL несёт светильник — благодарите его. Но не просите светильник стать солнцем.

Ибо технология без архитектуры есть самовар без воды: блестит, шумит, греется, собирает вокруг себя серьёзных людей, но чаю не будет.
А архитектура без технологии есть ангел без ботинок: прекрасен, но по российской распутице далеко не уйдёт.


Заключительное слово к главе 3 от Винни-Пуха 
*о слоях, правде и горшочке с данными*

Когда я был маленьким медведем, я думал, что если где-то есть горшочек, то в нём, конечно, мёд. Потом оказалось, что иногда в горшочке бывает не мёд, а очень важные данные, которые ещё только притворяются мёдом.

И вот я понял одну Простую Вещь.

Если данные только пришли, их нельзя сразу есть. Даже если они пахнут отчётом. Даже если Кролик говорит: “Пух, давай быстрее, у нас совещание”. Сначала нужно узнать, откуда они пришли, не пришли ли они два раза, не потеряли ли по дороге хвост, не называют ли одного и того же Пятачка тремя разными именами.

Потом данные должны посидеть в правильном месте и подумать, кто они такие. Это называется core, хотя я бы назвал это “место, где мёд перестаёт быть подозрительным”. Там выясняется, что считать правдой, что было вчера, что стало сегодня, и почему нельзя просто вытереть лапой старую запись.

А уже потом можно делать витрину. Витрина — это когда мёд разложили по маленьким баночкам, подписали, кому какую, и поставили так, чтобы Сова могла прочитать, а Иа не сказал: “Опять всё бессмысленно”.

Главное, что я понял: хранилище данных — это не большой шкаф. Это путь. Данные входят как шумные гости, потом становятся понятными, потом полезными, потом красивыми, а потом кто-нибудь обязательно спрашивает: “А почему эта цифра такая?” И если хранилище хорошее, оно не краснеет, а отвечает.

И ещё: если вы строите отчёт прямо из staging, то это как есть мёд прямо из улья, не спросив пчёл. Иногда получается сладко. Но чаще пчёлы возражают.



;
Глава 4

4.1. Что такое источник в банке

Источник данных в банке — это не таблица, не файл и не “место, откуда мы забираем данные”. Это прежде всего операционная система, в которой что-то происходит до хранилища. Там открываются счета, оформляются кредиты, меняются лимиты, проходят платежи, создаются заявки, заполняются анкеты, блокируются карты, обновляются контакты, фиксируются решения сотрудников и автоматических правил. Хранилище приходит позже. Источник живёт раньше.

В этом смысле источник — место рождения события. Не обязательно место рождения истины, но место первого следа. Клиент пришёл в офис, менеджер создал заявку. Клиент совершил платёж, процессинг зафиксировал авторизацию. Банк начислил проценты, core banking изменил состояние договора. Оператор колл-центра обновил телефон, CRM сохранила новое значение. Каждая такая система создавалась не для того, чтобы радовать аналитика, а для того, чтобы выполнять свою операционную работу.

Это важно. Источник работает для бизнеса, а не для КХД. Core banking должен правильно провести операцию, рассчитать остаток, удержать договорную логику. CRM должна помочь продавать, обслуживать и помнить коммуникации. Карточная система должна быстро принять решение по авторизации. Платёжная система должна провести перевод в нужном регламенте. Внешний сервис должен отдать ответ по своему протоколу. Ни одна из этих систем не обязана изначально думать категориями будущей витрины, отчёта, исторической аналитики или единой модели данных.

Поэтому данные источника всегда локальны. Они правдивы внутри контекста, для которого были созданы. Статус клиента в CRM может означать готовность к коммуникации, а не юридическое состояние отношений с банком. Статус договора в core banking может отражать бухгалтерскую или операционную логику, а не маркетинговую активность. Карточная авторизация может быть событием намерения списать деньги, но не окончательной финансовой проводкой. Один и тот же человек в разных системах может быть клиентом, заявителем, держателем карты, заёмщиком, контактным лицом или контрагентом. Это не обязательно противоречие. Это разные локальные взгляды.

Контекст источника определяет смысл его полей. Поле `status` почти никогда не означает просто “статус”. Нужно знать статус чего, в каком процессе, кто его меняет, когда он обновляется, какие значения возможны, какие значения устарели, какие появляются только в исключениях. Поле `amount` может быть суммой операции, суммой авторизации, суммой проводки, суммой заявки, лимитом, остатком, задолженностью или плановым платежом. Пока мы не знаем процесс, в котором поле родилось, мы знаем только его имя.

Именно поэтому первый шаг работы с источником — не подключиться к нему, а понять его жизнь. Какие события он фиксирует? Какие сущности для него главные? Кто вносит данные: человек, автоматический процесс, внешняя система? Когда данные меняются? Есть ли история изменений или только текущее состояние? Что считается ошибкой, а что нормальным исключением? Как источник сам понимает свои статусы, даты, суммы, идентификаторы?

Хранилище данных не должно презирать источник за его локальность. Источник не обязан быть универсальным. Он был построен для конкретной работы и часто хорошо делает именно её. Проблема начинается тогда, когда локальное значение выносят за пределы его контекста и объявляют общей истиной. Тогда CRM-статус становится “статусом клиента вообще”, карточная авторизация — “финансовой операцией вообще”, текущая запись справочника — “историей клиента вообще”. Так рождаются ошибки не из грязи, а из преждевременного обобщения.

В банке почти нет данных “просто о клиенте”, “просто о счёте”, “просто о договоре”. Всегда есть данные, созданные какой-то системой для какой-то цели. И прежде чем они попадут в КХД, нужно услышать эту цель. Не для того, чтобы слепо принять логику источника, а чтобы правильно перевести её на язык хранилища.

Источник приносит не окончательную правду, а первое свидетельство своего процесса. И чем лучше мы понимаем этот процесс, тем меньше шансов принять локальный шёпот системы за голос всей реальности.


4.2. Основные типы банковских источников

Банк не является одной системой. Снаружи он может выглядеть как единый организм: клиент открывает приложение, видит счёт, карту, кредит, платежи, предложения, уведомления. Но внутри этот организм состоит из множества систем, каждая из которых отвечает за свой участок жизни. У каждой свой язык, свой ритм, своя память и своя степень близости к деньгам.

Первый и самый важный источник — Core Banking. Это сердце банковской операционной логики: счета, договоры, остатки, проводки, кредиты, проценты, графики платежей, статусы финансовых продуктов. Из него в КХД обычно приходят сведения о счетах, движениях денег, кредитных договорах, остатках на даты, начислениях, платежах, закрытиях, просрочке. Если нужно понять финансовое состояние клиента перед банком, Core Banking часто оказывается ближе всех к основанию факта.

Но даже Core Banking не стоит романтизировать. Он не всегда удобен для аналитики, может быть тяжёлым, старым, чувствительным к нагрузке, устроенным вокруг операционных процессов, а не вокруг будущих отчётов. Его данные важны, но они тоже требуют понимания: что является проводкой, что является остатком, когда закрывается операционный день, как отражаются сторно и корректировки, какие статусы означают юридическое состояние, а какие техническое.

CRM — другая природа. CRM хранит отношения с клиентом: анкеты, контакты, сегменты, обращения, коммуникации, продажи, заявки, реакции на предложения. Из неё приходят ФИО, телефоны, email, адреса, согласия на коммуникации, история касаний, клиентские сегменты, признаки интереса к продуктам, данные менеджеров и каналов. CRM часто ближе к человеку как к собеседнику банка, но дальше от финансовой истины. Она может знать, кому звонить, но не обязательно точно знать, какой остаток на счёте.

Данные CRM особенно часто несут след человека. Менеджер что-то ввёл руками, исправил телефон, сократил имя, создал дубль, выбрал статус из списка, который понимал по-своему. Поэтому CRM полезна и опасна одновременно. Она даёт богатый клиентский контекст, но требует осторожности: контактные данные, сегменты и история коммуникаций не всегда обладают той же строгостью, что финансовые записи в core banking.

Карточные системы отвечают за жизнь карт: выпуск, привязку к счетам, лимиты, блокировки, авторизации, операции в торговых точках, каналы, устройства, MCC-коды, статусы транзакций. Из них приходят данные о картах, авторизациях, отказах, лимитах, блокировках, merchant-данных, иногда о географии и устройстве операции. Эти системы живут быстро. Они часто фиксируют намерение операции раньше, чем окончательная финансовая проводка появляется в core banking.

Именно поэтому карточные данные нельзя автоматически приравнивать к бухгалтерской реальности. Авторизация может быть отменена, сумма может измениться после клиринга, валюта может пройти конвертацию, операция может зависнуть в промежуточном состоянии. Для антифрода карточная система может быть главным источником, потому что скорость важнее окончательности. Для финансового отчёта она может быть только частью пути, а окончательное слово останется за проводками и остатками.

Платёжные системы — ещё один слой движения денег. СБП, SWIFT, БЭСП, внутренние платёжные шлюзы, внешние переводы, межбанковские сообщения. Они приносят сведения о входящих и исходящих платежах, контрагентах, назначениях, статусах обработки, ошибках, возвратах, времени прохождения, каналах. Их особенность — асинхронность. Платёж может иметь несколько состояний, идти через внешние контуры, возвращаться, уточняться, зависеть от регламентов и подтверждений.

Платёжные данные часто важны не только суммой, но и маршрутом. Откуда пришло, куда ушло, через какой канал, в каком статусе, кто контрагент, когда было отправлено и когда окончательно принято. Для КХД это источник не только финансовых фактов, но и следов процесса. И эти следы нужно читать осторожно: “отправлено” ещё не значит “завершено”, “принято” не всегда значит “проведено”, “возвращено” может относиться к другому моменту времени, чем исходный платёж.

Есть внешние источники. БКИ — бюро кредитных историй — дают информацию о кредитной жизни клиента за пределами банка: внешние кредиты, просрочки, запросы, история обязательств. ЦБ и регуляторные источники дают справочники, курсы, ставки, БИК, классификаторы, нормативные данные. Внешние справочники помогают нормализовать банки, отрасли, территории, валюты, юридические признаки, контрагентов. Такие источники не создаются внутри банка, но сильно влияют на его картину мира.

Внешние источники отличаются тем, что банк не управляет их внутренней логикой. Он получает данные по заданному протоколу, расписанию и правилам внешнего владельца. У них могут быть свои задержки, версии, форматы, ограничения, платность, регламенты обновления. Их нельзя исправить напрямую. С ними нужно договариваться через контракт, мониторинг и правильную интерпретацию.

Если смотреть на все эти источники вместе, становится видно: каждый приносит в хранилище не “данные вообще”, а свой кусок банковской реальности. Core Banking приносит финансовое основание. CRM — клиентский контакт и коммерческую историю. Карточные системы — быструю жизнь платёжного инструмента. Платёжные системы — движение денег между контурами. Внешние источники — сведения, которые банк не может породить сам, но должен учитывать.

Именно поэтому источник нельзя оценивать только по названию. Нужно спрашивать: какие сущности он знает, какие события фиксирует, какая у него скорость, есть ли история, кто владеет данными, можно ли ему доверять по конкретному полю, как он ошибается, как меняется, как отдаёт данные наружу. В КХД важен не просто список систем, а понимание их ролей.

Банковские источники похожи на хор, где каждый голос поёт свою партию. Ошибка архитектора — требовать, чтобы один голос спел всё произведение. Задача хранилища — услышать, кто именно сейчас говорит, о чём он имеет право говорить и где его голос должен быть сопоставлен с другими.


4.3. Источник истины на уровне поля

В банке очень хочется найти главную систему. Такую, на которую можно указать пальцем и сказать: вот здесь правда. Все остальные пусть молчат, подчиняются, сверяются и не создают лишних вопросов. Это желание понятно: архитектура любит центр, отчётность любит определённость, люди устают от споров между источниками. Но банковская реальность редко устроена так просто. Одна система может быть главной в одном смысле и вторичной в другом. Она может отлично знать остаток, но плохо знать актуальный телефон. Может точно хранить договор, но не знать последнюю коммуникацию с клиентом. Может быстро видеть авторизацию, но ещё не знать окончательную проводку.

Поэтому зрелое КХД выбирает источник истины не для системы целиком, а для поля, атрибута, контекста. Не “CRM главнее Core Banking” и не “Core Banking всегда прав”. Правильнее спрашивать: для какого именно значения мы ищем авторитет? Если речь об остатке на счёте, вероятно, главным будет Core Banking или бухгалтерский контур. Если речь о телефоне клиента для коммуникации, CRM или мобильный банк могут быть ближе к живой реальности. Если речь о статусе карты, карточная система знает его лучше, чем клиентская анкета. Если речь о курсе валюты, источник может быть внешним и регламентированным. Истина в КХД часто распределена не по системам, а по смысловым областям.

Это особенно важно для клиентских данных. Один и тот же клиент может иметь имя в CRM, паспортные данные в АБС, телефон в мобильном приложении, адрес в анкете кредита, сегмент в маркетинговой системе, риск-класс в скоринговом контуре. Если все эти атрибуты просто собрать в одну строку, возникнет вопрос: какой источник победил и почему? ФИО из CRM может быть удобнее для обращения, но паспортные данные из банковского ядра могут быть юридически значимее. Телефон, введённый менеджером пять лет назад, может быть менее надёжен, чем номер, подтверждённый в мобильном банке вчера. Источник истины определяется не гордостью системы, а природой атрибута.

Конфликт источников в такой картине перестаёт быть исключением. Он становится нормальной архитектурной ситуацией. Если CRM говорит, что клиент активен, а Core Banking говорит, что все договоры закрыты, это не обязательно означает, что одна система “сломалась”. Возможно, они говорят о разных активностях. Для CRM активность — возможность коммуникации или наличие отношений в продажном контуре. Для Core Banking активность — наличие финансового продукта или договора. Оба значения могут быть правдивы, но их нельзя смешивать без определения смысла.

То же происходит со статусами. “Открыт”, “активен”, “закрыт”, “заблокирован”, “приостановлен” — эти слова похожи на универсальные, но внутри разных систем они могут означать разные этапы процесса. Карта может быть заблокирована для операций, но счёт при этом открыт. Заявка может быть одобрена, но договор ещё не выдан. Платёж может быть отправлен, но не завершён. Если хранилище не знает, какой источник отвечает за какой статус, отчёты начинают спорить не потому, что данные плохие, а потому что слова были взяты без их родного контекста.

Источник истины на уровне поля — это способ превратить конфликт в правило. Мы заранее фиксируем: для остатка используем один источник, для контактного телефона — другой, для юридического имени — третий, для маркетингового сегмента — четвёртый. Иногда правило может быть сложнее: если телефон подтверждён в мобильном банке, берём его; иначе берём CRM; если паспортные данные прошли верификацию, они имеют приоритет; если статус пришёл из системы, владеющей процессом, он главнее производного статуса в соседнем контуре. Важно не то, чтобы правило всегда было простым. Важно, чтобы оно существовало и было объяснимым.

Без такого правила `core` начинает превращаться в поле дипломатической войны. Каждый отчёт выбирает источник по-своему. Один аналитик берёт телефон из CRM, другой из мобильного приложения, третий из последней заявки. В одном месте клиент активен, в другом неактивен, в третьем “активен при условии”. Пользователи видят расхождения и думают, что хранилище ненадёжно. Хотя проблема не в том, что источники конфликтуют. Проблема в том, что архитектура не решила, как читать этот конфликт.

При этом источник истины не обязательно вечен. Правила могут меняться. Банк может внедрить MDM, изменить процесс подтверждения контактов, перенести мастер-данные в отдельную систему, признать новый контур владельцем определённого атрибута. Поэтому важно хранить не только само значение, но и происхождение: откуда оно пришло, когда, по какому правилу было выбрано, какая версия правила действовала. Иначе сегодняшнее решение начнёт переписывать вчерашнюю логику незаметно.

В этом смысле “главная система” — слишком грубая метафора. В КХД ближе другая картина: разные источники являются свидетелями разных сторон реальности. Один видел деньги. Другой видел клиента. Третий видел карту. Четвёртый видел платёж. Пятый видел внешнюю кредитную историю. Архитектура должна не назначить одного вечного царя, а построить порядок свидетельств: кто говорит о чём, в каких условиях, с каким приоритетом и какой ответственностью.

Поэтому вопрос “какая система правильная?” почти всегда нужно уточнять: правильная для чего? Для какого поля, какого процесса, какой даты, какого решения? Только после этого конфликт источников перестаёт быть шумом и становится материалом для архитектуры. Хранилище данных взрослеет тогда, когда перестаёт искать одну главную систему на все случаи и начинает точно знать, кто имеет право говорить каждую конкретную фразу.


4.4. Типовые дефекты источников

У источников редко бывает злой умысел. Они не просыпаются утром с намерением испортить хранилище. Они просто живут своей операционной жизнью: принимают платежи, создают заявки, проводят договоры, хранят анкеты, обслуживают пользователей, меняются под новые бизнес-процессы. Дефекты данных чаще рождаются не из хаоса, а из того, что система была создана для действия, а не для будущего аналитического покоя.

Самый простой дефект — пропуск. Поле есть, но значения нет. Телефон пустой, паспорт не заполнен, дата рождения отсутствует, статус не проставлен, код продукта не передан. Пропуск кажется грубой ошибкой, но он не всегда означает одно и то же. Значение может быть действительно неизвестно. Может быть неприменимо. Может быть скрыто по правилам доступа. Может быть не заполнено из-за ошибки интерфейса. Может быть потеряно при выгрузке. Для источника это иногда допустимое состояние, для отчёта — провал, для регуляторной формы — инцидент, для модели — шум или смещение.

Дубли — другой классический дефект. Технический дубль возникает, когда одна и та же запись пришла дважды: повторная выгрузка файла, retry API, сбой доставки, повторное сообщение в потоке. Семантический дубль тоньше: один и тот же клиент создан дважды, один договор живёт в двух представлениях, один контрагент записан с разными именами. Технический дубль можно искать по ключу и отпечатку строки. Семантический дубль требует понимания сущности. Он уже ведёт нас к теме идентичности: не всякое повторение видно глазами базы.

Странные значения часто выглядят как почти нормальные. Дата рождения в будущем. Сумма с неправильным знаком. Возраст в двести лет. Статус, которого нет в документации. Валюта, похожая на код, но не существующая в справочнике. Номер телефона из трёх цифр. В таких случаях источник вроде бы передал значение, но значение сопротивляется смыслу. И здесь важно не только поймать ошибку, но и понять её природу: это мусор, редкий допустимый случай, новый бизнес-сценарий или изменение правил, о котором хранилище ещё не знает?

Отдельная боль — разные форматы одного смысла. Один источник пишет дату как `2026-04-29`, другой как `29.04.2026`, третий как строку с временем и часовым поясом. Один называет активный статус `ACTIVE`, другой `A`, третий `1`, четвёртый `OPEN`. Для человека это может быть очевидным сходством. Для системы это разные значения. Если их не нормализовать осознанно, один и тот же смысл будет распадаться на несколько технических масок.

Но разные форматы — это не только про внешний вид. Иногда за похожими значениями скрываются разные смыслы. `OPEN` в одной системе может означать открытый договор, в другой — открытую заявку, в третьей — незавершённый процесс. Поэтому нормализация не должна быть слепым переводом “всё похожее в одно”. Сначала нужно понять контекст, потом строить словарь соответствий. Иначе мы не очищаем данные, а красиво смешиваем чужие языки.

Отсутствие истории — дефект, который не всегда заметен сразу. Источник может хранить только текущее состояние: текущий телефон, текущий статус, текущий сегмент, текущий адрес. Для операционной работы этого иногда достаточно. Но КХД часто нужно знать, как было раньше. Когда клиент стал VIP? Когда договор изменил статус? Какой телефон был на момент заявки? Какой сегмент действовал в день операции? Если источник перезаписывает прошлое текущим значением, хранилище должно быть готово само создавать историю с момента подключения. Иначе вчерашние отчёты начнут жить сегодняшней памятью.

Задержки — ещё один нормальный дефект внешнего мира. Данные могут приходить позже события. Платёж прошёл сейчас, а в выгрузке появится через час. Карточная авторизация видна сразу, а окончательная проводка позже. Внешний источник обновляется раз в сутки. Файл формируется после закрытия операционного дня. Если хранилище не знает ритм источника, оно может перепутать отсутствие данных с отсутствием события. Это разные вещи: в одном случае мира не было, в другом он просто ещё не доехал.

Изменения задним числом особенно болезненны. Источник может сегодня прислать корректировку за вчера, сторно за прошлую неделю, изменение статуса с датой действия в прошлом, уточнение суммы за закрытый период. Для операционной системы это может быть нормальная бухгалтерская или процессная жизнь. Для витрин и отчётов это вызов: уже опубликованный период должен быть пересмотрен или зафиксирован? Нужно ли пересчитать окно? Как объяснить изменение числа, которое вчера считалось окончательным? Данные не всегда идут вперёд по календарю. Иногда прошлое приходит позже настоящего.

Наконец, изменение схемы без предупреждения. Источник добавил колонку, убрал поле, изменил тип, начал присылать новый статус, поменял смысл старого признака, расширил справочник, изменил порядок колонок в файле. Самое опасное здесь — тихое изменение. Если pipeline падает, это неприятно, но честно. Если он молча принимает новую схему и начинает интерпретировать её по старым правилам, ошибка может долго выглядеть как нормальная работа.

Все эти дефекты не означают, что источник плохой. Они означают, что источник живой. Он создавался для конкретного процесса, меняется вместе с бизнесом, зависит от людей, регламентов, внешних систем, технических ограничений. Задача КХД — не требовать от источника идеальной чистоты, а знать его болезни, измерять их, документировать и строить pipeline так, чтобы дефекты не превращались в скрытую ложь.

Типовые дефекты источников — это не список ужасов, а карта внимательности. Пропуски говорят о неполноте. Дубли — о повторении или неопределённой идентичности. Странные значения — о нарушении ожиданий. Разные форматы — о множестве языков. Отсутствие истории — о короткой памяти. Задержки и задние числа — о сложном времени. Изменение схемы — о движении самого источника.

Хорошее хранилище не удивляется этим дефектам. Оно ждёт их. Не с цинизмом, а с профессиональным спокойствием. Потому что источник без дефектов — это либо миф, либо источник, который ещё не профилировали.


4.5. Идентичность на входе

Один из самых болезненных вопросов источников звучит почти по-детски: кто это? Не что произошло, не сколько денег прошло, не какой статус стоит в поле, а кто именно стоит за записью. В банковских системах один и тот же человек может появляться под разными именами, идентификаторами и ролями. В CRM он клиент с одним `client_id`, в core banking — владелец счёта с другим идентификатором, в кредитном конвейере — заявитель, в карточной системе — держатель карты, в БКИ — субъект кредитной истории, во внешнем платёжном контуре — контрагент. Для бизнеса это может быть один человек. Для источников — несколько разных записей.

Эта проблема возникает не потому, что системы глупы. Каждая из них создавалась для своей задачи. CRM нужно быстро завести клиента и начать коммуникацию. Core banking нужно открыть договор и обслуживать финансовые обязательства. Карточной системе нужно выпустить карту и авторизовать операции. Кредитному конвейеру нужно обработать заявку. Каждая система создаёт идентификатор в своём мире. Этот идентификатор может быть правильным локально, но он не обязан быть глобальным именем человека во всём банке.

Поэтому разные ID одной сущности — нормальная банковская реальность. Один клиент может иметь несколько записей в CRM из-за ошибок ввода или разных каналов обращения. В одной системе он записан по паспорту, в другой по телефону, в третьей по номеру договора. Где-то сменил фамилию, где-то телефон, где-то документ. Где-то данные обновились, где-то нет. И если хранилище просто перенесёт все эти идентификаторы как есть, оно получит не единого клиента, а россыпь отражений, каждое из которых будет частично правдиво.

Идентичность нельзя откладывать слишком далеко. Если на входе не обнаружить, что одна сущность живёт в разных источниках под разными именами, дальше модель начнёт строиться на неверном количестве объектов. Один клиент станет тремя клиентами. Его остатки разделятся. Его риски разойдутся. Его продукты окажутся в разных профилях. Его кредитная нагрузка будет занижена. Его маркетинговые предложения станут хаотичными. Его подозрительные операции могут не собраться в один сценарий. Ошибка идентичности редко остаётся локальной. Она расползается по всей аналитике.

Сопоставление может быть точным. Например, совпали паспортные данные и дата рождения. Совпал ИНН для юридического лица. Совпал уникальный номер договора, который действительно глобален. Совпал внутренний master ID, если такая система уже существует. Точное сопоставление удобно тем, что даёт высокую уверенность. Но в реальности точные признаки часто отсутствуют, заполнены не везде, записаны в разных форматах или содержат ошибки. Паспорт может быть введён с пробелом, телефон — с восьмёркой вместо семёрки, ФИО — с сокращением, дата рождения — пустой.

Тогда появляются вероятностные признаки сопоставления. Система смотрит не на один абсолютный ключ, а на совокупность похожести: фамилия, имя, дата рождения, телефон, паспорт, адрес, email, ИНН, история продуктов, связь с договором. Каждый признак сам по себе может быть слабым, но вместе они дают вероятность. Иванов Иван Петрович с той же датой рождения и тем же телефоном, но другим client_id, вероятно, тот же человек. Но “вероятно” — не то же самое, что “доказано”. Поэтому у таких сопоставлений должны быть уровни уверенности, пороги, правила ручной проверки и история решений.

Перед сопоставлением данные почти всегда нужно нормализовать. Нельзя сравнивать телефоны, пока один записан как `+7 903...`, другой как `8-903...`, третий без кода страны. Нельзя сравнивать паспорта, если в одном источнике серия и номер разделены, а в другом склеены. Нельзя сравнивать ФИО, если где-то есть отчество, где-то инициалы, где-то латиница, где-то опечатка. Нормализация здесь не создаёт истину, но убирает шум, который мешает увидеть возможное совпадение.

Однако идентичность — не только техническая задача. Это архитектурное решение с последствиями. Если мы склеили две записи, все связанные факты начинают сходиться к одной сущности. Если мы ошиблись, мы смешали жизни двух разных людей. Если мы не склеили, мы раздробили одного клиента на несколько частей. Обе ошибки опасны. Ошибочная склейка может быть хуже пропущенной, потому что она создаёт ложное единство. Пропущенная склейка создаёт неполноту. Поэтому identity resolution требует осторожности, следов и возможности пересмотра.

В этой теме важно слово “на входе”. Это не значит, что вся MDM-логика должна быть полностью решена до первой загрузки. Но уже при знакомстве с источником нужно увидеть, есть ли проблема идентичности. Какие ключи использует система? Уникальны ли они? Есть ли дубли? Можно ли связать сущности с другими источниками? Какие поля помогают сопоставлению? Какие поля ненадёжны? Есть ли изменения документов, телефонов, фамилий? Есть ли master-система или её только предстоит создать? Если эти вопросы не задать до построения модели, потом придётся перестраивать уже готовые связи.

Модель данных, построенная без понимания идентичности, похожа на город, где дома пронумеровали до того, как решили, где улицы. Сначала кажется, что всё можно связать позже. Но потом отчёты, витрины, ключи, факты, измерения и правила уже опираются на старые идентификаторы. Исправление становится болезненным: нужно пересчитывать историю, менять mapping, объяснять, почему количество клиентов изменилось, почему вчерашний отчёт показывал одно, а сегодняшний другое. Поэтому проблему идентичности лучше обнаружить рано, пока она ещё является вопросом, а не долгом.

Хорошее КХД не обязано сразу идеально знать каждого клиента. Но оно должно понимать, что идентичность не дана источником окончательно. Источник приносит локальное имя. Хранилище должно решить, как это имя связано с другими именами того же мира. И чем раньше эта работа начнётся, тем меньше шансов, что в глубине системы поселится множество “разных” клиентов, которые на самом деле всё это время были одним человеком.


4.6. Способы подключения

Когда мы поняли, что за источник перед нами, какие данные он создаёт, как ошибается, как хранит идентичность и где может быть источником истины, появляется практический вопрос: как забрать из него данные? На схеме это снова выглядит простой стрелкой. Но способ подключения задаёт ритм всей дальнейшей жизни данных: как часто они будут приходить, насколько свежими, насколько полными, насколько воспроизводимыми, насколько дорогими для источника и хранилища.

Первый и самый древний способ — файл. Источник формирует выгрузку и кладёт её в условленное место: SFTP, сетевую папку, объектное хранилище, архив, письмо, иногда почти ритуальный каталог с именем “incoming”. Файл может быть CSV, XML, JSON, Excel, фиксированной ширины, zip-архивом, набором частей. Его достоинство — материальность. Файл можно увидеть, сохранить, переиграть, посчитать строки, проверить размер, сравнить контрольную сумму. Он хорошо подходит для пакетных поставок, ночных выгрузок, старых систем, регуляторных и справочных данных, где свежесть в секунды не нужна.

Но файл приносит и свои слабости. Он может прийти позже, не прийти, прийти дважды, оказаться неполным, быть перезаписанным, поменять структуру, содержать лишнюю строку заголовка, странную кодировку, не ту дату в имени. Файловый обмен требует мониторинга не меньше, чем потоковая интеграция. Если файл должен прийти до 02:00, система должна знать, пришёл ли он, полностью ли записан, сколько в нём строк, соответствует ли он ожидаемому формату. Иначе хранилище будет ждать письмо, которое, возможно, уже потерялось в коридоре.

Второй способ — `API`. `API` — Application Programming Interface, программный способ обмена между системами. Через API можно запросить клиентов, заявки, статусы, курсы, результаты проверки, страницы справочника, изменения за период. API хорош гибкостью: можно получать данные по параметрам, работать с внешними сервисами, забирать небольшие порции, вызывать методы по расписанию или по событию. Для CRM, БКИ, внешних справочников, сервисов скоринга и современных приложений API часто оказывается естественным языком.

Но API редко отдаёт данные как спокойная река. У него есть лимиты запросов, пагинация, токены, версии методов, таймауты, коды ошибок, ограничения доступа, разные представления одних и тех же сущностей. Если нужно забрать миллион клиентов по тысяче на страницу, важно понять, что происходит, если источник изменился между первой и последней страницей. Если запрос упал на середине, нужно знать, откуда продолжить. Если API вернул успешный ответ с пустым списком, это конец данных или временный сбой? API требует не только кода, но и протокола доверия.

Третий способ — `CDC`. `CDC` — Change Data Capture, передача изменений из источника в downstream-слои. Вместо того чтобы каждый раз забирать всё, система читает поток изменений: вставки, обновления, удаления, новые версии строк. Это особенно полезно для больших и активных источников, где полная выгрузка слишком тяжела, а свежесть важна. CDC позволяет снизить нагрузку на источник и получать изменения близко к моменту их возникновения.

Но CDC сложен. Он требует понимания журнала изменений, позиции чтения, повторных событий, удаления, порядка доставки, задержки потока, изменения схемы. Он приносит не готовую таблицу, а последовательность следов. Если pipeline не умеет идемпотентно обрабатывать повторы, хранить offset, восстанавливаться после сбоя и понимать, что делать с delete-событиями, CDC быстро превращается из “современного подключения” в источник тонких ошибок. Свежесть здесь покупается дисциплиной.

Есть ещё ручная выгрузка. Её обычно стесняются, но она встречается чаще, чем хотелось бы. Человек открывает систему, нажимает кнопку, выгружает Excel, кладёт файл в папку, иногда перед этим “чуть-чуть правит”. На старте проекта ручная выгрузка может быть временной опорой: нужно быстро посмотреть данные, источник ещё не подключён, автоматизация не готова. Но как постоянный способ поставки она опасна. В ней слишком много человеческого состояния: отпуск, ошибка, забытый фильтр, переименованный файл, открытая и пересохранённая таблица, непонятная версия.

Ручная выгрузка может жить только как временная мера с ясным сроком, журналом и контролем. Иначе хранилище начинает зависеть не от системы, а от привычки конкретного человека. Это особенно плохо для отчётности: данные должны приходить по правилам, а не по памяти сотрудника.

Выбор способа подключения зависит не от моды, а от природы источника. Если объём большой и изменения частые, полная файловая выгрузка может быть слишком тяжёлой. Если свежесть критична, файл раз в сутки не подойдёт. Если источник старый и не умеет API, файловый обмен может быть самым честным вариантом. Если источник внешний и отдаёт данные только через API, нужно строить устойчивую работу с лимитами и пагинацией. Если источник критичен и обновляется постоянно, CDC может быть оправдан, но только при зрелом pipeline.

Здесь важно учитывать четыре свойства: объём, частоту, свежесть и надёжность. Объём говорит, сколько данных нужно переносить. Частота — как часто они меняются или нужны хранилищу. Свежесть — насколько быстро событие должно стать видимым в КХД. Надёжность — насколько уверенно источник отдаёт данные и насколько легко восстановиться после сбоя. Один источник может быть маленьким, но требовать высокой свежести. Другой огромным, но спокойно жить в ночном пакетном режиме. Третий может быть важным, но настолько нестабильным, что сначала нужно строить контроль поставки, а уже потом думать о красивой аналитике.

Способ подключения также влияет на downstream. Файл хорошо дружит с batch_id и replay. API требует запоминать страницы, параметры, время запроса и ответы. CDC требует хранить offset, порядок событий и повторяемость обработки. Ручная выгрузка требует особенно строгого контроля происхождения, потому что в неё уже вмешался человек. То, как данные входят, определяет то, как потом их можно объяснять.

Поэтому подключение источника — это не техническая мелочь в конце анализа. Это часть архитектурного понимания источника. Мы не просто выбираем транспорт. Мы выбираем форму отношений между внешней системой и КХД: письма раз в день, разговор по запросу, поток изменений или человеческий ритуал. У каждой формы есть своя цена и свой риск.

Хороший архитектор не спрашивает: “Какой способ подключения современнее?” Он спрашивает: “Как этот источник живёт, как часто меняется, сколько данных отдаёт, насколько срочно они нужны, как он ломается и как мы будем доказывать, что получили всё?” После этого способ подключения часто становится очевиднее. Данные входят в хранилище не абстрактно. Они входят тем способом, который соответствует их природе.


4.7. Профилирование источника

Документация источника — это гипотеза. Иногда хорошая, иногда полезная, иногда написанная умными людьми в момент, когда система действительно так работала. Но всё равно гипотеза. В банке документация может отставать от реальности на месяцы и годы. Поле, которое описано как обязательное, на практике заполнено только в 70% строк. Статус, которого нет в описании, давно живёт в данных. Тип поля изменился, но документ не обновили. Справочник расширился, но об этом знают только два разработчика и один отчёт, который уже сломался.

Поэтому перед тем как верить источнику, его нужно профилировать. Профилирование — это первое системное знакомство с реальными данными. Не с тем, что источник обещает, а с тем, что он фактически отдаёт. Это не полноценная загрузка в КХД, не построение модели и не глубокое расследование всех смыслов. Это первичный осмотр: сколько данных, как устроены ключи, где пропуски, какие диапазоны дат, какие форматы, какие статусы, какие странности сразу бросаются в глаза.

Первый вопрос — объём. Сколько строк в таблице или выгрузке? Сколько приходит в день, неделю, месяц? Растёт ли источник равномерно или скачками? Есть ли историческая глубина или только текущий срез? Объём нужен не ради красивого числа. Он определяет будущую физику pipeline: можно ли забирать данные полностью, нужны ли инкременты, как долго будет идти загрузка, сколько места займёт staging, какие партиции понадобятся, насколько тяжёлыми будут первичные проверки.

Второй вопрос — ключи. Есть ли поле, которое источник считает уникальным? Действительно ли оно уникально? Бывают ли дубли? Меняется ли ключ со временем? Может ли одна бизнес-сущность иметь несколько ключей? Может ли один ключ относиться к разным сущностям в разных периодах или продуктах? Документация может уверять, что `client_id` уникален, но профилирование должно спокойно спросить данные: а вы сами с этим согласны?

Третий вопрос — пропуски. Где `NULL`, пустые строки, нули вместо неизвестных значений, технические заглушки вроде `1900-01-01`, `9999-12-31`, `UNKNOWN`, `N/A`? Особенно важно смотреть обязательные поля: идентификаторы, даты, суммы, статусы, ссылки на другие сущности. Пропуск сам по себе ещё не всегда ошибка, но он всегда вопрос. Он должен быть объяснён: это допустимо, временно, связано с процессом, результат дефекта или сигнал, что источник не может быть использован для данного атрибута без дополнительной логики.

Четвёртый вопрос — даты. У источника почти всегда есть несколько времён: дата события, дата создания записи, дата изменения, дата выгрузки, дата загрузки в КХД. Нужно посмотреть минимумы и максимумы, неожиданные будущие даты, слишком старые значения, пустоты, скачки, периоды без данных. Даты показывают не только время, но и память источника. Источник может хранить десять лет истории, а может только вчерашний срез. Он может присылать события задним числом. Может обновлять запись сегодня, но с датой действия в прошлом. Без понимания дат невозможно честно строить историю и отчётность.

Пятый вопрос — форматы. Телефон, паспорт, ИНН, счёт, БИК, код продукта, валюта, email, адрес, номер договора — всё это может выглядеть структурированно, но на практике содержать пробелы, дефисы, латиницу, кириллицу, разные маски, лишние символы, сокращения, старые правила. Формат важен не как эстетика, а как возможность сопоставления. Если телефоны нельзя сравнить, клиенты не склеятся. Если паспорт записан по-разному, точное совпадение не сработает. Если статусы не нормализованы, отчёт будет считать разные маски разными состояниями.

Шестой вопрос — статусы и категориальные значения. Нужно посмотреть, какие значения реально встречаются и с какой частотой. Часто именно здесь обнаруживается настоящая жизнь источника: редкие статусы, технические состояния, устаревшие коды, значения, которые появляются только при ошибках, комбинации, которых не было в документации. Частотный анализ статусов похож на прослушивание языка системы. До него мы знаем словарь из документа. После него начинаем слышать, как источник говорит на самом деле.

Все эти проверки должны привести не просто к ощущению “источник грязный” или “источник нормальный”. Результатом должен стать паспорт источника. В нём фиксируется, что это за система, какие сущности она отдаёт, какие таблицы или методы доступны, кто владелец, какой способ подключения, как часто обновляются данные, какие ключи заявлены и какие реально работают, какие поля критичны, где есть пропуски, какие статусы встречаются, какие даты используются, какие известны дефекты, какие правила нужны для загрузки и интерпретации.

Паспорт источника — это не бюрократический документ ради документа. Это способ не начинать каждый разговор заново. Когда через месяц кто-то спросит, почему мы не верим полю `phone` из этой системы, ответ должен быть не “кажется, там было плохо”, а “в профилировании обнаружено 18% пустых значений, 7 форматов, 3% некорректных номеров, источник не подтверждает актуальность телефона”. Когда в модели данных возникает вопрос о ключе, паспорт должен сказать: ключ заявлен как уникальный, но в реальности есть такие-то дубли и такие-то исключения.

Профилирование — первый акт недоверия. Но это не враждебное недоверие. Это профессиональное недоверие, без которого невозможно уважение. Мы не обвиняем источник заранее. Мы просто не позволяем документации заменить наблюдение. Мы спрашиваем данные, как они живут, и слушаем ответ. Иногда источник окажется чище, чем ожидалось. Иногда грязнее. Иногда он покажет странность, которая на самом деле является важным бизнес-правилом. Иногда обнаружит проблему, о которой владельцы источника сами не знали.

Хорошее профилирование меняет характер подключения. После него pipeline строится не вслепую. Мы уже знаем, какие поля проверять, где ждать дублей, какие даты важны, какие статусы требуют словаря, какие атрибуты нельзя считать надёжными, какой объём придёт ночью, где понадобятся reject-правила, где нужна MDM-логика, где источник может быть source of truth, а где только дополнительным свидетельством.

Именно поэтому профилирование должно происходить до серьёзного проектирования модели. Если сначала построить красивую модель, а потом открыть реальные данные, источник может вежливо разрушить половину предположений. Лучше позволить ему сделать это раньше, когда разрушение ещё является знанием, а не переработкой готовой архитектуры.

Документация говорит, как источник должен выглядеть. Профилирование показывает, как он выглядит, когда никто не смотрит. Для КХД важнее второе. Потому что хранилище будет жить не с обещанием источника, а с его реальным поведением.


4.8. Итог: источник нужно понять до того, как ему поверить

Источник данных приносит в КХД не истину, а локальное свидетельство. Это, пожалуй, главная мысль всей главы. Внутри своей операционной жизни источник может быть точным, полезным и даже незаменимым. Но как только его данные выходят за пределы родного процесса и попадают в хранилище, они требуют перевода. Нужно понять, кто говорит, о чём говорит, в каком контексте, с какой задержкой, с какими ошибками и с каким правом быть услышанным.

У каждого источника есть происхождение. Данные не возникают в пустоте. Их создают операции, интерфейсы, люди, автоматические правила, внешние сервисы, регламенты, ночные выгрузки, журналы изменений. Если КХД не знает происхождение данных, оно слишком быстро начинает обращаться с ними как с безличной таблицей. Но строка из CRM, строка из core banking, строка из карточного процессинга и строка из внешнего справочника — это разные виды свидетельств. Их нельзя судить одной меркой.

У каждого источника есть дефекты. Пропуски, дубли, странные значения, разные форматы, отсутствие истории, задержки, задние числа, изменения схемы. Это не исключительные катастрофы, а нормальная жизнь данных до хранилища. Зрелая архитектура не строится на надежде, что источник будет идеальным. Она строится на знании того, как именно он неидеален. Если дефекты видимы, их можно учитывать. Если они скрыты, они рано или поздно станут расхождениями в отчётах.

У каждого источника есть ритм. Один отдаёт данные раз в сутки после закрытия дня. Другой меняется каждую минуту. Третий присылает корректировки задним числом. Четвёртый работает только через файл по расписанию. Пятый доступен через API, но ограничивает частоту запросов. Шестой даёт поток изменений, но требует контроля задержки и повторов. Если не понимать этот ритм, хранилище будет путать пустоту с опозданием, свежесть с полнотой, текущее состояние с закрытым периодом.

У каждого источника есть границы. Он знает не всё. CRM может хорошо помнить коммуникации, но плохо отвечать за финансовый остаток. Core banking может быть главным для счёта и договора, но не знать последнего подтверждённого телефона. Карточная система может первой увидеть авторизацию, но не всегда является окончательной бухгалтерской правдой. Внешний справочник может быть эталоном для кода, но не объяснять внутренний бизнес-смысл банка. Хорошее КХД не требует от источника быть всем. Оно понимает, в какой области источник имеет право говорить уверенно.

Поэтому подключение источника начинается не с выбора протокола. Файл, API, CDC, ручная выгрузка — это уже способы разговора. Но до выбора способа нужно понять собеседника. Какие данные он создаёт? Для какого процесса? Кто владелец? Какие поля надёжны? Какие требуют проверки? Где история? Где текущий срез? Как приходят исправления? Как часто меняется схема? Какие признаки позволяют понять полноту поставки? Что считается ошибкой, а что нормальным исключением?

Если эти вопросы заданы, подключение становится осмысленным. Мы выбираем не просто транспорт, а форму отношений с источником. Файл подходит там, где важна пакетность и воспроизводимость. API — там, где нужен управляемый запрос к сервису. CDC — там, где важна свежесть изменений и допустима сложность потока. Ручная выгрузка — только как временный костыль, который должен знать, что он костыль. Способ подключения должен вытекать из поведения источника, а не из моды или привычки команды.

Источник нужно понять до того, как ему поверить. Это не цинизм. Это уважение к реальности. Источник не обязан быть готовым фрагментом КХД. Он приносит голос своего процесса. Задача хранилища — услышать этот голос, сохранить его происхождение, увидеть дефекты, определить границы доверия и только потом вплести его в общую картину.

Если глава 3 говорила о внутренней архитектуре хранилища, то глава 4 показывает, почему эта архитектура вообще нужна. Внешний мир приходит не в виде аккуратной истины, а в виде множества локальных свидетельств. КХД становится ценным не потому, что умеет механически забрать эти свидетельства, а потому что умеет понять, где каждому из них место.


;
Глава 5

5.1. ETL/ELT как мастерская преобразований

В предыдущей главе мы смотрели на pipeline как на движение: данные пришли, были приняты, проверены, переданы дальше, где-то упали, где-то повторились, где-то были отвергнуты и отправлены в отдельную судьбу. Здесь мы меняем угол зрения. Нас теперь интересует не сама дорога, а мастерская, в которой данные перестают быть только принесённым извне сырьём и получают форму, пригодную для жизни внутри хранилища.

ETL и ELT различаются порядком действий. ETL — это `Extract, Transform, Load`: сначала извлечь данные, потом преобразовать, потом загрузить. ELT — это `Extract, Load, Transform`: сначала извлечь и загрузить, а преобразовывать уже внутри хранилища. Но для архитектора важнее не мода на три буквы и не спор о том, какая последовательность современнее. Важнее другой вопрос: где именно данные теряют свою первичную дикость и становятся частью управляемого смысла.

Сырой факт приходит из источника в своей родной одежде. Он может быть странным, неполным, повторившимся, написанным в чужом формате, привязанным к локальному коду, понятному только одной системе. У него уже есть значение, но это значение ещё не обязательно пригодно для общего языка КХД. Преобразование начинается там, где мы не просто переносим строку из одного места в другое, а решаем: что она означает, с чем она связана, можно ли ей доверять, какую версию считать действующей, какой справочник к ней применить, какой статус открыть или закрыть.

Поэтому трансформация — это не косметика. Исправить формат даты, сопоставить клиента, убрать технический дубль, привести статус к общему справочнику, рассчитать признак просрочки — всё это не просто удобные операции над колонками. Это вмешательство в судьбу факта. До трансформации он был свидетельством источника. После трансформации он становится частью согласованной картины. И именно здесь нужно быть особенно осторожным: если перенос данных отвечает на вопрос “где строка находится”, то преобразование отвечает на вопрос “кем она стала”.

Хороший ETL/ELT-процесс не должен превращаться в тёмную комнату, где данные исчезают в одном виде и появляются в другом уже без следов происхождения. Он должен оставлять возможность понять, почему значение изменилось, какое правило сработало, откуда пришёл исходный факт и можно ли повторить этот путь ещё раз. Иначе мастерская превращается не в место ремесла, а в место алхимии: на входе была строка, на выходе появился отчёт, а между ними клубится благовонный дым уверенности, в котором никто уже не различает, где знание, а где удачно пережившая ночь ошибка.


5.2. Очистка и нормализация

Очистка начинается с простых вещей, которые только кажутся простыми. В одном поле дата приходит как `2026-04-29`, в другом как `29.04.2026`, в третьем как строка, которую человек когда-то набрал руками и забыл поставить ноль. Телефон может быть записан с восьмёркой, с семёркой, с пробелами, со скобками, без кода страны. Валюта может жить как `RUB`, `810`, `руб.`, а иногда как молчаливая уверенность источника, что “и так понятно”. Статус может быть числом, буквой, словом, внутренним кодом или историческим обломком старой системы, которую уже никто не любит, но все боятся трогать.

На этом уровне данные ещё не лгут. Они просто говорят на разных диалектах. Очистка не должна относиться к ним как судья к преступнику. Её задача — убрать шум, не уничтожив свидетельство. Пробел можно обрезать, формат даты можно привести к общему виду, странное значение можно вынести на проверку, но нельзя делать вид, что источник всегда хотел сказать именно то, что нам удобно услышать. В хорошем КХД исходная форма не исчезает бесследно: если значение было исправлено, приведено, отвергнуто или заменено, у этого действия должен остаться след.

Нормализация идёт чуть глубже. Она не просто вытирает пыль с поверхности, а переводит разные языки источников в общий словарь хранилища. Если одна система говорит “active”, другая “A”, третья “1”, а четвёртая хранит статус в виде фразы “договор действует”, нормализация пытается понять, можно ли всё это считать одним и тем же состоянием. Но здесь начинается опасная зона: одинаковая форма ещё не означает одинаковый смысл. Два похожих статуса могут быть юридически разными, два разных кода могут означать один бизнес-факт, а один и тот же код в разных системах может жить разной жизнью.

Поэтому очистка и нормализация — это не санитарная уборка перед настоящей работой. Это уже работа со смыслом. Мы берём данные такими, какими их породил источник, и осторожно приводим их к форме, в которой они смогут разговаривать с другими данными. Не стать красивее. Не стать удобнее любой ценой. А стать понятнее, не потеряв память о своей первой, местной, иногда кривой, но всё ещё важной речи.

5.3. Дедупликация и выбор версии

Повтор в данных не всегда является ошибкой. Иногда строка пришла дважды из-за повторной выгрузки. Иногда источник отправил тот же факт ещё раз, потому что не умеет иначе подтверждать своё состояние. Иногда две записи выглядят одинаково, но на самом деле говорят о разных событиях. А иногда наоборот: две строки отличаются мелочами, но описывают один и тот же факт, который просто прошёл через систему в двух одеждах.

Поэтому дедупликация начинается не с удаления, а с вопроса: что именно мы считаем повтором? Технический дубль обычно проще: совпали ключи, совпали значения, совпала партия загрузки или контрольная сумма. Такая строка похожа на эхо, которое вернулось в коридор и притворилось новым голосом. Смысловой дубль сложнее. Там совпадение может быть неполным: тот же клиент, тот же договор, та же дата, почти та же сумма, но разные идентификаторы, разные времена загрузки, разные признаки источника. И тут уже нельзя действовать грубо. Машина видит строки, но архитектор должен видеть ситуацию.

Выбор версии — ещё более тонкое место. Если пришли две записи об одном и том же объекте, какая из них главная? Более поздняя? Из более надёжного источника? Та, где заполнено больше полей? Та, что прошла проверку? Та, что подтверждена внешним справочником? Ответ не должен быть спрятан в случайном порядке сортировки или в привычке разработчика брать “последнюю строку”. Правило выбора версии должно быть явным, объяснимым и устойчивым. Иначе хранилище начнёт принимать решения, о которых никто не знает.

Опасность дедупликации в том, что она выглядит как наведение порядка. Кажется, что дубль — лишний, а лишнее надо удалить. Но иногда “лишняя” строка является единственным следом сбоя, повторной доставки, исправления задним числом или конфликта между источниками. Удалить её физически — значит не только очистить таблицу, но и стереть возможность расследования. Поэтому зрелая дедупликация не уничтожает память. Она отделяет рабочую версию от сырого множества, но оставляет возможность вернуться и увидеть: вот здесь факт пришёл дважды, вот здесь версии спорили, вот здесь система выбрала одну из них не потому, что забыла остальные, а потому что имела правило.

5.4. Сопоставление и обогащение

Данные редко приходят в хранилище уже собранными в целый мир. Факт операции знает сумму и дату, но может плохо знать клиента. Договор знает свой номер, но не всегда знает продукт в том виде, в котором его понимает аналитика. Клиент в одной системе живёт под одним идентификатором, в другой — под другим, в третьей — почти тем же именем, но с иной датой рождения, старым паспортом и странной тенью ручного ввода. Чтобы факт стал пригодным для анализа, его нужно привязать к контексту: к клиенту, договору, счёту, продукту, подразделению, каналу, периоду, справочнику.

Сопоставление — это момент, когда строка перестаёт быть одинокой. Она получает соседей и родственные связи. Для этого используются ключи, справочники, mapping-таблицы, то есть таблицы соответствий между разными кодами и идентификаторами. Иногда связь точная: один код продукта однозначно соответствует одному продукту в общем справочнике. Иногда связь требует правила: код менялся во времени, филиал был переименован, продукт переехал из одной категории в другую. Иногда связь вероятностная: клиент похож на уже известного клиента, но стопроцентного доказательства нет. И тогда важно не превращать догадку в камень.

Здесь рядом появляется MDM (`Master Data Management` — управление мастер-данными). MDM нужен там, где одна и та же сущность рассыпана по нескольким системам и должна быть собрана в управляемую идентичность. Это не волшебная кнопка “склеить клиентов”. Это дисциплина: какие признаки считаются сильными, какие слабыми, какой источник имеет приоритет, как хранится связь между локальными идентификаторами и мастер-записью, что делать при конфликте, как не потерять историю изменений. Без этой дисциплины сопоставление быстро превращается в домашнюю магию: сегодня записи соединились потому, что очень хотелось, завтра разъединились потому, что кто-то поменял правило.

Обогащение продолжает эту же логику. Мы добавляем к факту контекст: название продукта, сегмент клиента, регион, риск-категорию, принадлежность к группе, признак зарплатного проекта, внешний рейтинг. Но обогащение должно не прятать неопределённость, а делать её видимой. Если связь точная — хорошо. Если связь приблизительная — это должно быть видно. Если справочник устарел — это не повод молча подставить первое подходящее значение. Хорошее обогащение не делает данные наряднее ради отчёта. Оно помогает факту занять место в общей картине, не заставляя его притворяться более уверенным, чем он есть.

5.5. Расчётные признаки и бизнес-правила

На каком-то этапе данных становится недостаточно в том виде, в котором они пришли. У факта уже есть дата, сумма, клиент, договор, продукт, источник, но бизнесу нужно не только видеть его тело. Нужно понимать его состояние. Просрочен ли договор. Активен ли клиент. Относится ли операция к подозрительной. Попадает ли сумма в нужную категорию. Наступила ли дата события. Изменился ли риск. Так появляются расчётные признаки: флаги, статусы, категории, суммы, даты, уровни, отметки, маленькие знаки на поверхности данных, по которым потом будет двигаться аналитическое мышление.

На вид это может казаться технической работой. Взять поле, применить условие, записать результат. Но именно здесь техническая трансформация начинает переходить в бизнес-логику. Если мы рассчитываем признак просрочки, мы уже не просто меняем формат значения. Мы утверждаем, с какого дня договор считается проблемным. Если определяем категорию клиента, мы решаем, какие признаки важнее других. Если строим статус заявки, мы выбираем, какое событие считать последним значимым. В этот момент код перестаёт быть только кодом и становится формой договорённости о реальности.

Поэтому одно и то же правило нельзя размазывать по отчётам. Если один дашборд считает активного клиента так, другой иначе, а третий использует старую версию условия, хранилище вроде бы продолжает работать, но общий язык начинает расползаться. Формально все отчёты правы, потому что каждый честно применил свою формулу. Смыслово они уже живут в разных мирах. И чем дольше такое продолжается, тем труднее потом понять, где возникло расхождение: в данных, в логике, в витрине или в том тихом месте, где кто-то однажды скопировал условие и немного поправил его “под задачу”.

Расчётный признак — это маленький договор между аналитикой и данными. Он говорит: в таких условиях мы будем считать объект вот таким. Не потому, что это абсолютная истина, сошедшая с неба, а потому, что для этой задачи, этого слоя, этой версии правил мы принимаем именно такую интерпретацию. Хороший признак должен быть устойчивым, объяснимым и воспроизводимым. Он не обязан быть вечным, но обязан иметь память: по какому правилу рассчитан, когда это правило изменилось, почему старый результат отличался от нового. И тогда данные не просто получают дополнительные колонки. Они получают артикуляцию — способность произносить то, что раньше было спрятано между строк.

5.6. Банковские паттерны трансформаций

В банковских данных особенно хорошо видно, что факт не всегда остаётся неподвижным. Он может стареть, отменяться, переоцениваться, менять юридическое и аналитическое значение без того, чтобы исчезнуть физически. Банк живёт не только событиями, но и последствиями событий. День прошёл — и договор стал другим. Курс изменился — и стоимость стала другой. Ошибка обнаружилась — и прошлое не исчезло, но рядом с ним появилось обратное движение. Поэтому банковские трансформации часто работают не с голой строкой, а с временем, ответственностью и следом.

Просрочка — один из самых выразительных примеров. Вчера договор мог быть обычным, сегодня он уже требует внимания, завтра попадёт в другую категорию риска. Никакой новый договор при этом не родился. Клиент не нажал кнопку “стать проблемой”. Просто время пересекло границу, заранее заданную правилом. Трансформация здесь делает видимым то, что не лежало в источнике одной готовой колонкой. Она смотрит на дату платежа, дату фактического поступления, календарь, условия договора, возможно — на реструктуризацию или перенос срока, и превращает молчание времени в признак состояния.

Сторно показывает другую природу банковского прошлого. Ошибочный факт нельзя просто вырвать из ткани данных, как будто его не было. Он мог попасть в отчёт, повлиять на баланс, стать частью сверки, оставить след в соседних системах. Поэтому вместо исчезновения возникает обратное движение. Факт получает пару, тень, зеркальный жест. И хранилище должно уметь видеть не только итоговую тишину, где плюс и минус взаимно погасли, но и саму драму: было событие, потом было признано, что оно не должно действовать, затем появился корректирующий ход. Для анализа часто важен не только результат, но и сама история исправления.

Переоценка ещё тоньше. Здесь может не быть новой операции в привычном смысле. Никто не выдал новый кредит, не открыл новый счёт, не совершил платёж. Но изменилась стоимость, курс, ставка, риск, модель, внешнее условие. Физического события нет, а финансовый смысл сдвинулся. В таких случаях трансформация похожа на перевод часов в комнате, где никто не двигался, но все предметы вдруг оказались в другом времени. Она заставляет хранилище признать: данные могут меняться не только потому, что пришла новая строка, но и потому, что изменился взгляд, через который старая строка оценивается.

Просрочка, сторно и переоценка важны не как три отдельных рецепта, а как три напоминания о природе банковской реальности. Здесь прошлое не лежит спокойно. Оно доначисляется, уточняется, отменяется, пересчитывается, переименовывается, иногда возвращается с печатью и требует нового места в таблице. Хорошая трансформация не пытается сделать это прошлое простым. Она делает его управляемым: показывает, что изменилось, почему изменилось, каким правилом было обработано и какой след оставило после себя.

5.7. Итог: хорошая трансформация оставляет след

Хорошая трансформация не пытается притвориться, что данных до неё не существовало. Она не говорит: “вот новая правильная форма, а всё прежнее забудьте”. Наоборот, зрелое хранилище помнит, откуда пришёл факт, каким он был сначала, через какие правила прошёл, что в нём было очищено, что нормализовано, с чем он был сопоставлен, какой признак был рассчитан и почему итоговое значение выглядит именно так.

Это особенно важно потому, что трансформация всегда несёт власть. Она может объединить две записи в одну, выбрать одну версию из нескольких, присвоить клиенту категорию, признать договор просроченным, отнести операцию к подозрительной, пересчитать стоимость, закрыть старое состояние и открыть новое. Каждое такое действие выглядит техническим только снаружи. Внутри него живёт решение о смысле. И если это решение невозможно объяснить, то хранилище начинает не хранить знание, а производить уверенность без памяти.

След трансформации нужен не для бюрократии. Он нужен для доверия. Когда отчёт расходится с ожиданием, команда должна иметь возможность пройти назад: увидеть исходное свидетельство, правило, момент обработки, версию справочника, выбранную запись, отброшенную запись, рассчитанное значение. Не для того, чтобы наказать конкретную строку, а чтобы понять, где именно смысл свернул с дороги. Если этот путь не восстановить, любая ошибка превращается в туман, а любой правильный результат становится счастливой случайностью.

Поэтому хорошая трансформация не делает данные просто красивыми. Красивые данные могут быть мёртвыми: гладкими, ровными, удобными, но лишёнными происхождения. Хорошая трансформация делает их честно пригодными для следующего слоя. Она меняет форму, но сохраняет память. Она добавляет смысл, но не стирает свидетельство. Она позволяет данным идти дальше — в core, витрину, отчёт, модель, управленческое решение — не как переодетый неизвестно кто, а как факт, у которого есть биография.
;
Глава 6

6.1. Зачем нужна модель данных

Модель данных нужна не потому, что архитектору хочется нарисовать ещё одну красивую схему. Она появляется там, где хранилище должно ответить на более глубокий вопрос: что именно существует в нашей банковской реальности? Есть ли клиент как самостоятельный объект или только как строка в заявке? Является ли договор событием, состоянием или долгоживущей сущностью? Остаток — это атрибут счёта, ежедневный снимок или факт наблюдения? Пока эти вопросы не заданы, таблицы могут уже существовать, данные могут уже загружаться, отчёты могут даже показывать какие-то числа. Но под ними ещё нет общей формы мышления.

Таблица сама по себе не является моделью. Таблица может быть просто местом, куда сложили поля. Иногда туда попадает всё сразу: клиент, договор, платёж, продукт, филиал, статус, остаток, дата последней операции, телефон, риск-категория и ещё несколько колонок, которые “попросили добавить срочно”. Сначала такая таблица кажется удобной. Всё рядом, всё видно, можно быстро написать запрос. Но постепенно она начинает пухнуть, как папка на столе человека, который решил больше никогда ничего не раскладывать по полкам. В ней повторяются имена, смешиваются уровни детализации, старые значения перетираются новыми, один договор размножается по числу платежей, один клиент существует в десятках строк, а изменение статуса становится трудно отличить от нового факта.

Опасность одной большой таблицы не только в технической тяжести. Главная опасность в том, что она скрывает природу данных. Если в одной строке одновременно живут клиент, договор, платёж и дневной остаток, то строка перестаёт отвечать на простой вопрос: чем она является? Она вроде бы про клиента, но повторяется из-за платежей. Вроде бы про договор, но содержит атрибуты продукта. Вроде бы про событие, но хранит состояние. Такая смесь может работать на первом отчёте, но чем больше вокруг неё строится логики, тем сильнее она превращается в болото, где каждый новый запрос вынужден заново угадывать, что означают старые колонки.

Модель данных проводит границу между хаосом источника и управляемой памятью КХД. Источник приносит данные так, как удобно его собственной жизни. Модель спрашивает: какие сущности мы признаём, какие события фиксируем, какие состояния наблюдаем, какие связи считаем значимыми, какую историю обязаны помнить. Она не делает мир проще насильно. Она делает его различимым. Благодаря модели хранилище перестаёт быть складом таблиц и становится устройством, которое понимает, где клиент, где договор, где факт движения денег, где состояние на дату, а где только временный след чужой системы.

Поэтому моделирование — это не этап “после техники”. Это один из центральных актов архитектуры. Pipeline может принести данные. ETL/ELT может преобразовать их. Витрина может сделать их удобными. Но модель решает, в какой форме данные будут жить между приходом и ответом. Она удерживает смысл, пока вокруг меняются источники, отчёты, правила, люди и очередные срочные просьбы “просто добавить колонку”.

6.2. Объект, событие, состояние

Чтобы построить модель, нужно научиться различать природу данных. Не все строки одинаковы. Одни описывают объекты, другие фиксируют события, третьи показывают состояние. Это различие кажется философским только на поверхности. На практике именно оно решает, будет ли модель устойчивой или начнёт ломаться при первой попытке посчитать что-то сложнее общего количества строк.

Объект — это то, что продолжается во времени. Клиент, договор, счёт, продукт, филиал, карта, залог, контрагент. У объекта есть идентичность: он может менять атрибуты, но при этом оставаться собой. Клиент может сменить паспорт, телефон, фамилию, сегмент, риск-категорию. Договор может изменить статус, ставку, срок, ответственного менеджера. Счёт может быть открыт, заблокирован, закрыт. Если модель не различает объект как долгоживущую сущность, она начинает путать изменение с рождением нового объекта или, наоборот, склеивать разные жизни в одну.

Событие — это то, что произошло. Операция, платёж, выдача кредита, изменение лимита, заявка, отказ, начисление комиссии, сторно, реструктуризация. Событие имеет момент или период, причину, участников, сумму, канал, результат. Его нельзя просто “обновить” как имя клиента. Если платёж был, а потом оказался ошибочным, зрелая модель не делает вид, что его никогда не существовало. Она должна уметь показать само событие и последующее обратное движение. События создают ткань движения: по ним видно, как банковская реальность не просто хранится, а происходит.

Состояние — это то, как объект выглядит в определённый момент или период. Остаток на счёте на конец дня. Статус договора на дату. Количество дней просрочки. Рейтинг клиента. Текущий сегмент. Доступный лимит. Состояние похоже на фотографию: оно может быть результатом многих событий, но само не всегда является событием. Остаток не “случился” так же, как платёж. Он был измерен или рассчитан на момент времени. Просрочка не всегда приходит отдельной строкой из источника; иногда она возникает потому, что дата обязательного платежа осталась позади, а нужного движения денег не произошло.

Проблемы начинаются там, где объект, событие и состояние смешивают в одной строке без ясного решения. Например, строка “кредитный договор за день” может одновременно содержать номер договора, данные клиента, сумму выдачи, текущий остаток, статус, последнюю операцию и признак просрочки. В отчёте это может выглядеть удобно, но в модели возникает путаница: одна строка описывает договор или день? Последнюю операцию или состояние портфеля? Клиента или его роль в договоре? Если завтра нужно будет восстановить, каким был статус договора неделю назад, или понять, почему изменилась просрочка, такая смесь начнёт сопротивляться.

Хорошая модель не запрещает соединять эти природы в витрине, когда это нужно для ответа. Но внутри КХД она старается понимать, что с чем имеет дело. Объекты хранят идентичность. События хранят движение. Состояния хранят наблюдение во времени. И когда эти три вещи различены, данные становятся спокойнее: клиент не размножается от каждого платежа, платёж не притворяется атрибутом договора, остаток не маскируется под вечную истину, а модель начинает говорить человеческим голосом: вот кто существует, вот что произошло, вот каким всё было на выбранный момент.

6.3. Гранулярность и ключи

О гранулярности мы уже говорили в главе о витринах, но в модели данных этот вопрос появляется раньше и глубже. Прежде чем выбирать поля, индексы, связи и названия таблиц, нужно ответить на самый простой и самый коварный вопрос: что означает одна строка? Один клиент? Один договор? Один счёт? Один платёж? Один день жизни договора? Один остаток на счёте в конце дня? Пока это не решено, модель похожа на комнату, где все предметы уже занесли, но никто не понял, где пол, где потолок, а где дверь.

Разные уровни детализации нельзя смешивать без последствий. Клиент живёт дольше договора. Договор живёт дольше платежа. Остаток на счёте может существовать как снимок на дату. Операция происходит один раз, но её последствия могут отражаться в состояниях много дней подряд. Если строка одновременно пытается быть клиентом, договором, операцией и днём, она становится удобной только на первый взгляд. Потом в ней начинается тихое расползание смысла: клиент повторяется из-за операций, договор повторяется из-за дат, остаток выглядит как свойство счёта, хотя на самом деле он принадлежит моменту времени.

Ключи нужны именно для того, чтобы удерживать идентичность внутри этого движения. `Business key` — это ключ из бизнес-реальности: номер договора, номер счёта, код клиента в источнике, номер карты, внешний идентификатор заявки. Он имеет смысл для системы, которая породила данные, и часто понятен людям. Но бизнес-ключ не всегда вечен и не всегда единственен: паспорт меняется, клиентские ID различаются по системам, договор может иметь локальный номер в одном контуре и другой номер в другом.

`Surrogate key` — это внутренний ключ хранилища, созданный уже внутри КХД. Он не обязан что-то значить для бизнеса. Его задача тише и строже: дать модели устойчивую точку крепления, чтобы разные версии, связи и атрибуты могли ссылаться на один и тот же объект, даже если внешние идентификаторы спорят между собой. `Technical key` — это ключ технического процесса: номер партии загрузки, хэш строки, идентификатор файла, дата загрузки, служебный номер события. Он помогает понять, как данные пришли, повторились, изменились или были обработаны.

Поэтому ключ — это не просто “номер в колонке”. Это способ сказать: вот этот объект остаётся собой, даже когда меняет признаки; вот эта строка является новым событием, а не повтором; вот это состояние относится к такому-то объекту на такую-то дату; вот эта запись пришла из такого-то источника и может быть проверена. Когда ключи выбраны плохо, модель теряет память о том, кто есть кто. Когда ключи выбраны хорошо, данные получают позвоночник: они могут двигаться, меняться, спорить, но не рассыпаться в безымянный песок.

6.4. Связи, mapping и MDM

В реальном банке сущности редко приходят в хранилище под одним именем. Один клиент может жить в CRM как потенциальный заёмщик, в Core Banking как владелец договора, в карточной системе как держатель карты, во внешнем бюро как человек с кредитной историей. Каждая система видит его по-своему, называет своим ID, хранит свой набор признаков и уверена, что именно её взгляд достаточно полон. Для модели это не ошибка, а исходное состояние мира: идентичность приходит раздробленной.

Связи в модели отвечают на вопрос не только “что с чем соединено”, но и “почему мы считаем это соединение допустимым”. Клиент связан с договором. Договор связан с продуктом. Счёт связан с клиентом, филиалом, валютой, иногда с пакетом обслуживания. Но между локальными идентификаторами источников и общей моделью почти всегда нужен мост. Этим мостом становятся mapping-таблицы: таблицы соответствий, где фиксируется, какой локальный ID из одной системы относится к какому объекту общей модели.

На первый взгляд mapping кажется скучной технической деталью. Но именно здесь часто скрывается одна из самых сложных частей архитектуры. Если CRM говорит, что клиент `123`, Core говорит, что заёмщик `A-987`, а карточная система приносит держателя `C-55`, кто решает, что это один человек? По паспорту? По телефону? По ФИО и дате рождения? По внутреннему золотому идентификатору? По ручному решению оператора? И что делать, если признаки совпали не полностью, если паспорт сменился, если телефон принадлежит супругу, если в одной системе ошибка, а другая просто молчит?

Здесь появляется MDM (`Master Data Management` — управление мастер-данными): не как модное слово, а как дисциплина сборки идентичности. MDM определяет, какие признаки считаются сильными, какие слабыми, какой источник имеет приоритет, как хранится мастер-запись, как фиксируются спорные случаи, как разводятся ошибочно склеенные сущности и как история соответствий переживает изменение правил. Это не просто “склеить дубли”. Это способ сделать так, чтобы объект в хранилище имел управляемую биографию, а не каждый раз заново рождался из очередного запроса.

Самая опасная форма этой логики — случайно спрятать её в `JOIN`. Сегодня один разработчик соединит клиента по паспорту, завтра другой добавит телефон, послезавтра третий исключит пустые даты рождения, и все трое будут уверены, что просто “подтянули клиента”. Но на самом деле каждый из них построит свою версию идентичности. Если правила сопоставления живут только внутри отдельных запросов, модель теряет центр. Она больше не знает, кто перед ней: один клиент, два похожих клиента или временное совпадение строк.

Поэтому связи, mapping и MDM должны быть частью явной модели, а не теневой привычкой запросов. Хранилище должно помнить не только саму связь, но и её происхождение: по какому правилу она возникла, когда была создана, какой источник её подтвердил, можно ли ей доверять полностью или только с оговоркой. Тогда общая модель не притворяется, что идентичность всегда очевидна. Она честно показывает: вот локальные имена, вот мосты между ними, вот мастер-сущность, а вот следы тех сомнений, без которых в банковских данных вообще редко рождается настоящая уверенность.

6.5. Data Vault: модель памяти

`Data Vault` можно понимать как модель памяти хранилища. Она не пытается сразу сделать данные удобными для отчёта. Её задача другая: принять сложную, изменчивую, противоречивую реальность источников и разложить её так, чтобы хранилище не теряло историю, не боялось новых систем и могло потом доказать, откуда взялся каждый фрагмент знания.

В основе Data Vault лежит простое разделение: ключ, связь и атрибут не должны жить в одной каше. `Hub` хранит бизнес-ключи сущностей: клиента, договора, счёта, продукта. Это место, где модель говорит: такой объект в мире существует или хотя бы однажды был засвидетельствован источником. `Link` хранит связи между объектами: клиент связан с договором, договор с продуктом, счёт с клиентом. `Satellite` хранит атрибуты и их изменения во времени: имя клиента, статус договора, сегмент, рейтинг, ставку, адрес, признак активности. Так Data Vault не смешивает “кто это”, “с чем это связано” и “каким это было”.

Сила такой модели особенно заметна в банке. Источники меняются, поля добавляются, старые системы спорят с новыми, один и тот же клиент приходит под разными именами, атрибуты исправляются задним числом. Data Vault не требует каждый раз переплавлять всю модель. Пришёл новый источник — можно добавить новый поток свидетельств. Появился новый атрибут — можно положить его в новый Satellite. Изменилась связь — можно зафиксировать новое состояние связи, не делая вид, что прежнего не было. В этом смысле Data Vault хорошо переносит время: он не требует, чтобы мир был чистым, прежде чем начать его помнить.

Но именно поэтому Data Vault плохо подходит для прямого чтения бизнесом. Для аналитика он часто выглядит как город архивов, где всё честно подписано, но до нужного ответа надо пройти несколько коридоров, открыть множество дверей и собрать смысл из частей. Чтобы ответить на простой вопрос, приходится соединять Hub, Link, Satellite, выбирать актуальные версии, учитывать источники, даты загрузки, интервалы действия. Это не язык дашборда и не язык руководителя, которому нужен ясный показатель к утру. Это язык внутренней памяти КХД.

Поэтому Data Vault не стоит воспринимать как готовый отчётный слой. Он ближе к складу свидетельств, где каждое свидетельство имеет место, дату, происхождение и связь с другими свидетельствами. Он не обязан быть красивым для чтения. Он обязан быть честным для восстановления. Его достоинство не в том, что бизнесу удобно смотреть на Hub и Satellite, а в том, что из этой памяти можно построить разные витрины, разные ответы, разные аналитические формы — и при необходимости вернуться назад, к тому моменту, где факт впервые вошёл в систему и начал свою вторую жизнь внутри хранилища.

6.6. Star Schema: модель ответа

Если Data Vault — это модель памяти, то `Star Schema`, или звёздная схема, — это модель ответа. Она строится не вокруг вопроса “как сохранить всё, что мы знаем”, а вокруг вопроса “как удобно и быстро ответить бизнесу”. В ней данные уже не лежат как архив свидетельств. Они собраны в форму, которая ближе к аналитическому мышлению: есть событие или измеряемое состояние, и есть контекст, в котором это событие нужно рассмотреть.

В центре звезды находится `fact` — таблица фактов. Факт хранит то, что можно измерять, считать, суммировать, сравнивать: сумму операции, остаток на дату, количество платежей, размер просрочки, объём выдач, комиссию, баланс портфеля. Вокруг факта располагаются `dimension` — измерения, то есть описания контекста: клиент, продукт, дата, филиал, канал, валюта, регион, сегмент. Факт отвечает на вопрос “что произошло или что измерено”, измерения помогают спросить “по кому, где, когда, через что, в каком разрезе”.

Именно поэтому звезда удобна для `BI` (`Business Intelligence` — инструменты аналитики и отчётности). Аналитик мыслит разрезами: продажи по продуктам, остатки по филиалам, просрочка по сегментам, операции по каналам, портфель по датам. Звёздная схема даёт этому мышлению естественную форму. Не нужно каждый раз идти в глубину архивной модели, выбирать версии атрибутов, собирать связи из десятков таблиц. Значительная часть смысла уже подготовлена: факт лежит в центре, измерения стоят вокруг него, запрос становится короче, отчёт — быстрее, витрина — понятнее.

Но у этой ясности есть цена. Звезда удобна потому, что уже приняла множество решений. Какой клиент считается актуальным. Какой продукт связан с договором. Какой статус показывать. Какой уровень детализации выбран. Какие атрибуты вынесены в измерения, какие метрики положены в факт, какая история сохранена, а какая упрощена ради ответа. Поэтому звезда не должна становиться единственным местом правды. Если вся память хранилища живёт только в витринных звёздах, то каждое новое бизнес-требование начинает ломать старую форму, а каждое упрощение постепенно превращается в потерю происхождения.

Хорошая Star Schema не спорит с Data Vault, а продолжает его работу на другом языке. Data Vault хранит сложность, звезда делает её пригодной для вопроса. Data Vault помнит, как факт пришёл и менялся, звезда показывает, как этот факт должен быть прочитан в аналитическом контексте. Один слой нужен, чтобы не потерять правду во времени. Другой — чтобы эта правда не осталась запертой в архиве, куда никто, кроме архитектора, не решается входить без фонаря и внутреннего приказа.

6.7. От памяти к ответу: эскиз кредитного портфеля

Возьмём кредитный портфель не как полный проект таблиц, а как упражнение в мышлении. В нём сразу видны основные формы банковской реальности: клиент, договор, продукт, платёж, остаток, просрочка. Если попытаться сложить всё это в одну широкую таблицу, она быстро станет похожа на витрину магазина после землетрясения: всё вроде бы на виду, но уже непонятно, что к чему относится. Поэтому сначала нужно не писать SQL, а разложить мир по природе вещей.

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

В core эта реальность должна жить как память. Здесь важно знать не только текущий портфель, но и то, каким он был вчера, месяц назад, на дату регуляторного отчёта, до исправления, после исправления, в момент первой загрузки. В core клиент не должен размножаться из-за каждого платежа, а договор не должен терять старые состояния только потому, что сегодня стал закрытым. Здесь модель удерживает бизнес-ключи, связи, историю атрибутов, события и снимки состояний. Она не обязана быть самой удобной для чтения, но обязана быть честной для восстановления.

В mart та же реальность принимает форму ответа. Например, витрина кредитного портфеля на день может сказать: одна строка — это договор на дату. В этой строке уже собраны клиентский сегмент, продукт, филиал, остаток, просрочка, статус, ставка, признак реструктуризации, риск-категория. Для отчёта это удобно: можно быстро посчитать портфель по регионам, просрочку по продуктам, долю проблемной задолженности по сегментам. Но важно помнить: такая строка не является всей истиной о договоре. Это подготовленный снимок, аналитическая форма, которая опирается на более глубокую память.

Так одна и та же банковская реальность получает две разные формы. В core она похожа на архив, где различены объекты, события, связи и изменения во времени. В mart она похожа на карту, которую можно открыть перед заседанием и быстро увидеть нужные контуры. Ошибка начинается там, где карту принимают за саму территорию или архив заставляют работать как дашборд. Модель кредитного портфеля должна позволять и помнить, и отвечать: сохранить сложность там, где она нужна, и дать ясность там, где бизнес задаёт вопрос.

6.8. Итог: модель должна объяснять, что именно существует

Хорошая модель делает данные понятными ещё до запроса. Когда человек смотрит на неё, он должен понимать: здесь живёт клиент, здесь договор, здесь событие платежа, здесь состояние на дату, здесь связь между объектами, здесь история атрибута, здесь подготовленный ответ для аналитики. Ему не нужно каждый раз угадывать, почему строка повторяется, какой уровень детализации выбран, откуда взялся статус и почему вчерашнее значение отличается от сегодняшнего.

Плохая модель делает обратное. Она заставляет каждый отчёт заново изобретать смысл. Один аналитик решает, что клиент определяется по CRM ID, другой — по номеру паспорта, третий — по последнему договору. Один отчёт берёт текущий статус, другой восстанавливает статус на дату, третий случайно смешивает оба подхода. Постепенно хранилище перестаёт быть общей памятью и становится набором частных догадок, каждая из которых работает ровно до первого серьёзного расхождения.

Модель должна удерживать четыре вещи: историю, связи, идентичность и уровень детализации. История отвечает на вопрос “как было тогда”. Связи отвечают на вопрос “что с чем связано”. Идентичность отвечает на вопрос “кто остаётся собой”. Гранулярность отвечает на вопрос “что означает одна строка”. Если хотя бы одна из этих опор исчезает, данные могут ещё физически лежать в таблицах, но смысл уже начинает проседать. И тогда никакой красивый отчёт не спасает: он будет похож на уверенный голос, читающий карту города, которого на самом деле никто не построил.

КХД начинается не с таблиц. Таблицы — это только тело. Настоящее начало в другом вопросе: какую реальность мы здесь удерживаем? Если мы не ответили на него, то все дальнейшие решения будут случайными: ключи, связи, витрины, правила, индексы, пайплайны. Если ответили — хранилище получает форму. И тогда данные могут приходить грязными, спорить между источниками, менять прошлое, требовать новых правил, но внутри системы у них будет место, имя, история и путь к ответу.

;
Глава 7

7.1. История не архив, а условие правды

Историзация нужна не потому, что хранилище должно быть сентиментальным. КХД не собирает прошлое как старые письма в коробке, перевязанные лентой, чтобы однажды кто-то растрогался над адресом клиента двадцатилетней давности. История в хранилище нужна по более жёсткой причине: без неё система не может честно отвечать на вопросы, в которых присутствует время. А почти все серьёзные банковские вопросы именно такие.

Текущее значение почти всегда удобно. Оно одно, его легко показать, к нему легко присоединиться, его легко объяснить словами “сейчас так”. Но отчётность редко спрашивает только про “сейчас”. Она спрашивает: каким был сегмент клиента на дату выдачи кредита? Какой статус был у договора на конец месяца? Какая ставка действовала в момент заключения сделки? Каким был риск-рейтинг на дату расчёта резерва? Если система хранит только текущие значения, она начинает смотреть на прошлое сегодняшними глазами. И тогда отчёт за 2024 год внезапно узнаёт, что клиент уже был VIP, хотя в 2024 он ещё был обычным массовым клиентом. Не потому, что кто-то соврал. А потому, что прошлое было перезаписано настоящим.

Отчёт “на дату” — главный тест историзации. Если хранилище может восстановить состояние объекта на выбранный момент, значит у него есть память, пригодная для работы. Если не может, значит оно хранит не историю, а текущий фасад. Можно иметь множество таблиц, аккуратные ключи, красивую модель и быстрые витрины, но если вопрос “что мы знали тогда?” превращается в гадание, система не выдерживает собственного назначения. Она может показывать числа, но уже не может доказать, почему именно эти числа были правильными в тот момент.

Прошлое хранится не ради прошлого. Оно нужно для воспроизводимости. Воспроизвести отчёт — значит не просто заново выполнить запрос, а вернуть те условия, в которых результат был получен: нужные версии атрибутов, действующие связи, статусы, ставки, сегменты, правила. Без этого отчёт каждый раз незаметно переигрывается. Вчерашний показатель пересчитывается сегодняшним знанием, и система начинает вести себя как человек, который уверяет, что всегда так думал, хотя его собственные записи говорят обратное.

Именно поэтому историзация становится обязанностью core. В staging данные ещё являются свидетельством источника. В mart они уже подготовлены для ответа. А core должен удерживать согласованную память: что считалось верным, когда это стало верным, когда перестало быть верным и какое новое состояние пришло на смену. Здесь история перестаёт быть технической опцией и становится условием правды. Без неё хранилище помнит только настоящее, а настоящее, если его оставить без прошлого, очень быстро начинает выдавать себя за вечность.

7.2. Что именно нужно историзировать

Историзировать можно многое, но нельзя делать это вслепую. Если хранить каждое движение каждой мелочи как великую драму, модель распухнет и перестанет быть понятной. Если хранить слишком мало, отчёты начнут лгать о прошлом. Поэтому первый вопрос историзации не “как сделать SCD”, а “какие изменения действительно имеют значение”. История должна появляться там, где изменение влияет на отчётность, расчёты, регуляторные требования, юридическую интерпретацию или возможность расследования.

Чаще всего историзируют атрибуты объектов. У клиента это могут быть паспорт, адрес, сегмент, риск-рейтинг, признаки согласий, принадлежность к группе. У договора — статус, ставка, срок, сумма, график, дата закрытия, признак реструктуризации. У продукта — категория, условия, тариф, ставка, правила обслуживания. У счёта — статус, валюта, принадлежность, ограничения. Но даже здесь нельзя говорить “всё хранить одинаково”. Исправление очевидной опечатки и смена юридически значимого документа — это разные события. Одно можно перезаписать как ошибку ввода, другое обязано остаться в истории.

Историзировать нужно не только атрибуты, но и связи. Клиент может быть связан с договором не всегда одинаково: сегодня он заёмщик, завтра поручитель, в другом контуре представитель компании. Договор может менять продуктовую классификацию, клиент — сегмент, счёт — владельца или режим обслуживания. Если хранить только текущую связь, прошлые отчёты начнут подтягивать сегодняшние отношения к вчерашним фактам. И тогда старый кредит может оказаться в новом сегменте, старая операция — в новом филиале, а прошлый риск — в той группе, которой тогда ещё не существовало.

Отдельная зона — состояния. Статус договора, остаток на счёте, рейтинг клиента, количество дней просрочки, доступный лимит, состояние заявки. Некоторые состояния приходят из источника как готовые значения, другие рассчитываются внутри КХД. В обоих случаях важно понимать, что состояние имеет дату действия. Остаток без даты — почти бессмысленен. Просрочка без понимания момента расчёта превращается в туман. Рейтинг клиента может быть верным только в определённом периоде, а статус договора — только до следующего изменения или исправления.

Но полная история нужна не всегда. Если поле не влияет на решения, отчёты, контроль, регуляторику и расследования, его историзация может быть лишней тяжестью. Иногда достаточно хранить текущее значение. Иногда нужно хранить только предыдущую версию. Иногда нужна полная цепочка. Хорошая историзация начинается не с фанатичного сохранения всего, а с честного разговора о смысле изменения: если это значение изменится, кто пострадает от того, что мы не сможем увидеть старое? Если ответ есть — историю надо хранить. Если ответа нет — возможно, перед нами не память, а просто коллекционирование пыли.

7.3. SCD: разные отношения к прошлому

`SCD` (`Slowly Changing Dimensions` — медленно изменяющиеся измерения) звучит так, будто речь идёт о каких-то спокойных, почти сонных изменениях. На самом деле слово “медленно” здесь обманчиво. Важна не скорость. Важен способ, которым система относится к прошлому. Когда значение изменилось, мы можем сделать вид, что старого никогда не было. Можем признать, что старое было, но больше не действует. Можем хранить только один небольшой след. Разные типы SCD — это разные формы памяти.

`Type 0` — самый строгий и неподвижный вариант: значение не меняется. Оно фиксируется один раз и дальше считается частью исходной природы объекта. Например, дата рождения или дата заключения договора могут восприниматься как такие значения. Но даже здесь нужно быть осторожным: если источник прислал ошибку, а потом исправление, перед нами уже не “изменение реальности”, а исправление неправильного свидетельства. Поэтому Type 0 не означает, что ошибки невозможны. Он означает, что бизнес-смысл поля не предполагает обычной смены значения.

`Type 1` — перезапись без истории. Было одно значение, стало другое, старое исчезло из рабочей модели. Это удобно для исправления опечаток, технических ошибок, незначимых полей, где прошлое значение не нужно для отчётов и расследований. Type 1 похож на аккуратную правку в тексте: убрали лишнюю букву, и никто не обязан помнить, как именно она портила слово. Но если так обращаться с критичными банковскими атрибутами — сегментом клиента, паспортом, статусом договора, согласием, рейтингом, — система начнёт переписывать историю под вид текущего дня.

`Type 2` — главный рабочий механизм банковской историзации. Старое значение не уничтожается, а получает границы действия. Новое значение становится новой версией. В результате объект существует не как одна вечная строка, а как цепочка состояний: клиент был в одном сегменте, потом в другом; договор имел один статус, потом другой; продукт действовал с одной ставкой, потом с другой. Здесь прошлое не спорит с настоящим. Оно просто перестаёт быть текущим.

`Type 3` и другие варианты существуют, но в этой книге нам не нужно превращать их в отдельный зоопарк. Type 3 хранит ограниченный след прошлого, например текущее значение и предыдущее значение в одной строке. Это может быть полезно в редких отчётах, где нужен только один шаг назад, но такая форма быстро становится тесной, если изменений много. Есть и гибридные подходы, отдельные таблицы истории, специальные паттерны для сложных случаев. Но для понимания банковского КХД главное различие простое: где-то прошлое можно перезаписать, где-то нужно помнить полностью, а где-то достаточно оставить короткую тень.

Поэтому выбор SCD — это не выбор красивого технического шаблона. Это решение о памяти. Для каждого атрибута нужно спросить: изменяется ли он, влияет ли его прошлое значение на отчётность, нужно ли доказать состояние на дату, что произойдёт, если мы оставим только “сейчас”. И если прошлое имеет силу, Type 1 уже не подходит. Там, где вчерашнее значение участвует в сегодняшнем доверии к системе, нужна версия, а не перезапись.

7.4. SCD Type 2: версия как отрезок времени

`SCD Type 2` строится на простой идее: объект остаётся тем же, но его состояние меняется во времени. Поэтому в таблице появляется не одна строка на объект, а несколько строк на разные версии этого объекта. Каждая версия отвечает не на вопрос “что верно вообще”, а на вопрос “что было верно в таком-то промежутке времени”. Это очень маленький сдвиг в форме хранения, но именно он превращает таблицу из снимка настоящего в управляемую память.

Обычно у такой модели есть несколько опорных полей. `Business key` — бизнес-ключ, то есть идентификатор объекта из предметной области или источника: номер договора, ID клиента, код продукта. Он помогает понять, о каком объекте идёт речь. `Surrogate key` — внутренний ключ версии, созданный в КХД; он отличает одну версию объекта от другой. `valid_from` показывает, с какого момента версия действует. `valid_to` показывает, до какого момента она действует. `is_current` помогает быстро найти текущую версию, хотя сам по себе не заменяет временные границы.

Когда приходит изменение, старая версия не удаляется. Она закрывается: её `valid_to` получает дату окончания действия, а `is_current` перестаёт быть истинным. Затем вставляется новая версия с новым периодом действия. В этой новой строке сохраняется тот же бизнес-ключ, потому что объект остаётся тем же, но появляется новый внутренний ключ версии, потому что состояние уже другое. Так модель говорит: это всё ещё тот же клиент или договор, но уже не та же его историческая форма.

Важный вопрос — как понять, что изменение действительно произошло. Нельзя создавать новую версию только потому, что строка снова пришла из источника. Повторная доставка не равна новому состоянию. Обычно сравнивают значимые атрибуты: сегмент, статус, адрес, ставку, риск-рейтинг, признаки согласий. Если они не изменились, новая версия не нужна. Если изменились только технические поля загрузки, это тоже не повод плодить историю. Для такого сравнения часто используют хэш значимых атрибутов: компактный отпечаток содержания строки, который помогает понять, изменилась ли именно бизнесовая часть записи.

Версия в SCD Type 2 — это не дубль. Дубль повторяет то же самое и создаёт шум. Версия фиксирует законное состояние объекта во времени. Две строки с одним бизнес-ключом могут быть не ошибкой, а правильной историей: раньше клиент был в одном сегменте, теперь в другом; раньше договор был активен, теперь закрыт; раньше продукт относился к одной категории, теперь к другой. Удалить “лишнюю” строку в такой модели — значит не очистить таблицу, а вырвать кусок памяти.

Поэтому SCD Type 2 требует дисциплины. Временные интервалы не должны пересекаться. Между версиями не должно быть случайных дыр, если бизнес-смысл предполагает непрерывность. Текущая версия должна быть одна. Дата действия должна отличаться от даты загрузки, если изменение вступило в силу раньше или позже момента прихода данных. И всё это нужно не ради эстетики схемы. Это нужно для того, чтобы в любой момент можно было спросить систему: каким был объект тогда — и получить не сегодняшнее мнение о прошлом, а версию, которая действительно действовала в выбранный момент.

7.5. Point-in-time: как выбрать правильную версию

`Point-in-time` — это состояние на конкретный момент времени. В историзированной модели мало просто знать, что у клиента было несколько сегментов, у договора несколько статусов, а у продукта несколько ставок. Нужно уметь выбрать именно ту версию, которая действовала в момент события или отчёта. Иначе история вроде бы хранится, но пользоваться ею система не умеет. Это как иметь архив с датированными документами, но каждый раз брать с верхней полки последний лист и уверенно прикладывать его ко всем прошлым годам.

Главная ошибка здесь — путать дату события и дату загрузки. Дата загрузки говорит, когда запись вошла в хранилище. Дата события говорит, когда что-то произошло в бизнес-реальности. Клиент мог сменить сегмент 10 марта, источник мог прислать это 12 марта, а хранилище обработало изменение 13 марта ночью. Все три даты важны, но отвечают на разные вопросы. Если отчёт строится “как было 10 марта”, он не должен слепо смотреть на дату загрузки. Ему нужна версия, которая действовала в бизнес-времени.

Поэтому соединение факта с историческим измерением делается не просто по ключу, а по ключу и временному интервалу. Недостаточно сказать: “возьми клиента по `client_id`”. Нужно сказать: “возьми ту версию клиента, у которой дата события попадает между `valid_from` и `valid_to`”. В этом месте `JOIN` перестаёт быть механическим соединением таблиц и становится выбором правильного прошлого. Он не просто подтягивает атрибут. Он решает, каким объект был тогда.

Пример прост. Кредит выдан клиенту в январе 2024 года, когда клиент был в массовом сегменте. В 2025 году он стал VIP. Если отчёт за 2024 год присоединит текущий сегмент, он покажет этот кредит как выданный VIP-клиенту. Формально данные все настоящие: кредит настоящий, клиент настоящий, сегмент тоже настоящий. Ложь возникает не в значениях, а во времени. Система взяла правильное поле из неправильного момента.

В этом и состоит смысл point-in-time-подхода: каждое значение должно отвечать из своей даты. Факт приносит момент события, историческое измерение приносит цепочку версий, а модель должна найти их пересечение. Тогда отчёт за прошлый год перестаёт быть пересказом прошлого сегодняшним голосом. Он становится восстановлением того состояния, в котором прошлое действительно было прожито системой.

7.6. Поздние данные и исправления задним числом

В идеальном мире данные приходят вовремя, изменения вступают в силу в момент загрузки, источники не ошибаются, а прошлое ведёт себя прилично и не трогает живых. В банковском мире всё иначе. Запись может прийти позже ожидаемого момента. Исправление может относиться к прошлому месяцу. Источник может обнаружить старую ошибку, переслать состояние задним числом, дослать пропущенную операцию или изменить статус, который уже успел попасть в отчёт. Это называется `late-arriving data` — данные, пришедшие позже ожидаемого момента.

Поздние данные опасны не тем, что они поздние. Опасность в том, что они требуют изменить прошлое, не уничтожив память о том, как система жила до исправления. Если 20 апреля пришла информация, что статус договора должен был измениться ещё 10 апреля, нельзя просто обновить текущую строку и успокоиться. Нужно понять, какая версия действовала с 10 по 20 апреля, какие отчёты уже могли использовать старое состояние, нужно ли пересчитать витрину, какие интервалы закрыть и открыть заново.

Здесь появляется `backfill` — дозагрузка или пересчёт прошлого периода. Backfill нужен, когда новые данные или новые правила требуют вернуться назад и заново пройти часть пути. Это не хаотичное “перезапустим всё с начала времён”, а управляемое возвращение в выбранный интервал. Например, пересчитать последние семь дней, потому что источник часто присылает исправления с задержкой. Или пересобрать прошлый месяц, потому что пришла корректировка по договорам. Backfill — это способ признать, что прошлое иногда требует технического обслуживания.

Самая тонкая часть — временные интервалы. При исправлении задним числом легко создать дыру, когда на часть периода нет ни одной действующей версии, или пересечение, когда две версии одновременно претендуют на одну дату. И то и другое разрушает доверие к историзации. Если система не может однозначно выбрать версию на дату, значит история стала не памятью, а спором двух строк за право называться прошлым.

Поэтому поздние данные требуют дисциплины. Нужно различать, когда запись пришла, когда она должна действовать, какой период затрагивает, какие версии закрывает, какие открывает, какие витрины нужно пересчитать. Хорошая историзация умеет принять запоздалое свидетельство без паники. Она не стирает старую память, но аккуратно встраивает новую версию в нужное место времени. Так прошлое не переписывается грубо. Оно уточняется, оставляя след того, что когда-то система знала меньше, потом узнала больше и смогла честно пересобрать свой ответ.

7.7. Историзация в core, mart и Data Vault

Историзация меняет форму в зависимости от слоя. Это важный момент: одна и та же память не обязана выглядеть одинаково везде. В core история нужна как глубокий механизм восстановления. В mart — как удобная форма ответа. В Data Vault — как естественная часть устройства Satellite. Если перепутать эти роли, можно либо перегрузить витрины лишней сложностью, либо лишить хранилище той памяти, на которой держится доверие к отчётам.

Core хранит подробную память изменений. Здесь важно не только текущее значение, но и цепочка версий: когда атрибут начал действовать, когда перестал, из какого источника пришёл, каким правилом был принят, что было до него. Core должен позволять восстановить состояние объекта на дату, расследовать расхождение, пересчитать прошлый период, принять позднее исправление. Поэтому история в core обычно богаче, чем нужно конкретному отчёту. Она хранится не ради одного вопроса, а ради способности системы отвечать на разные вопросы без потери происхождения.

Mart выбирает удобную форму истории для ответа. Витрине не всегда нужно показывать всю цепочку изменений. Иногда ей нужен только актуальный слой: “клиенты сейчас”. Иногда нужен срез на дату: “портфель на конец дня”. Иногда нужна серия снимков: “остатки по дням за год”. Иногда в измерении витрины сохраняют SCD Type 2, чтобы факт мог соединяться с версией клиента или продукта на момент события. Но mart не должен притворяться, что его упрощённая форма является полной исторической памятью. Витрина готовит историю к употреблению, а не заменяет собой весь механизм памяти.

В Data Vault история обычно живёт в `Satellite`. `Hub` фиксирует бизнес-ключ, `Link` фиксирует связь, а Satellite хранит изменяющиеся атрибуты и их версии. Это делает Data Vault удобным для подключения новых источников и сохранения свидетельств во времени: у сущности может быть несколько Satellite из разных систем, с разной частотой обновления и разным набором атрибутов. Такая структура не всегда удобна для отчёта, зато хорошо показывает, откуда пришло значение и когда оно стало частью памяти хранилища.

Отдельно стоят snapshot-витрины. Snapshot — это снимок состояния на момент или период: например, ежедневный кредитный портфель, остатки на конец дня, статус договоров на отчётную дату. Такой снимок очень полезен для аналитики, потому что не заставляет каждый раз заново собирать состояние из глубокой истории. Но snapshot не заменяет историческую модель. Он фиксирует результат сборки на дату, а не обязательно объясняет весь путь, которым этот результат был получен. Если источник ошибся, правило изменилось или пришло позднее исправление, нужно понимать, как пересобрать snapshot из более глубокой памяти.

Поэтому правильный вопрос звучит не “где хранить историю”, а “какая форма истории нужна этому слою”. Core должен помнить подробно. Mart должен отвечать удобно. Data Vault должен принимать изменчивые свидетельства без разрушения модели. Snapshot должен давать быстрый снимок состояния. Когда эти роли различены, история перестаёт быть тяжёлым грузом и становится рабочим механизмом: в одном месте она хранит глубину, в другом превращается в ясный ответ.

7.8. Банковские сценарии историзации

Банковская историзация становится понятной не в абстрактных типах SCD, а в конкретных ситуациях, где ошибка времени меняет смысл отчёта. Самый простой пример — сегмент клиента на момент выдачи кредита. Клиент мог быть массовым в январе, стать VIP в июне, перейти в другой сегмент осенью. Если отчёт о выдачах за январь возьмёт текущий сегмент, он припишет прошлое сегодняшнему состоянию. В результате кредит будет выглядеть выданным не тому сегменту, которому он действительно был выдан в тот момент.

Ставка продукта на дату заключения договора работает похожим образом. Банк меняет условия, рынок движется, продуктовые линейки пересматриваются. Но кредит, заключённый в марте, должен анализироваться со ставкой, которая действовала в марте, а не с сегодняшней ставкой из справочника. Иначе средняя ставка выдач, доходность, маржинальность и сравнение периодов начнут незаметно смещаться. Историзация здесь удерживает не просто значение ставки, а экономический контекст сделки.

Статус договора на дату отчёта — ещё один критичный сценарий. Договор мог быть активен, затем выйти в просрочку, потом реструктурироваться, потом закрыться. Для отчётности важно не только текущее “закрыт” или “активен”, а состояние на конец конкретного дня или месяца. Особенно это важно для портфельных отчётов, резервов, NPL, управленческой отчётности. Если статус хранится только текущий, прошлые периоды начинают переписываться каждый раз, когда договор меняет жизнь.

Есть и более чувствительные поля: согласие клиента, паспорт, риск-рейтинг, признаки KYC, просрочка. Согласие важно потому, что нужно доказать, имел ли банк право на действие в конкретный момент. Паспорт и документы важны для юридической и регуляторной памяти. Риск-рейтинг нужен не только текущий, но и тот, на основании которого принимались решения. Просрочка показывает состояние обязательства во времени: сегодня она может быть погашена, но вчера она уже влияла на портфель и резервы.

Во всех этих сценариях историзация защищает систему от одной и той же ошибки: от желания смотреть на прошлое через текущее значение. Банк живёт во времени, и его данные тоже. Клиент меняется, договор меняется, продукт меняется, правила меняются, но отчёт должен понимать, в каком моменте он задаёт вопрос. Историзация даёт ему эту способность: не просто видеть значение, а видеть значение, действовавшее тогда.

7.9. Антипаттерны историзации

Первый и самый дорогой антипаттерн — перезаписывать критичные атрибуты. Сегмент клиента, паспорт, статус договора, согласие, риск-рейтинг, ставка продукта, принадлежность к филиалу могут казаться обычными полями, пока кто-то не попросит отчёт на прошлую дату. Если они перезаписаны, система уже не может честно сказать, что было тогда. Она может только взять сегодняшнее значение и вставить его в вчерашний контекст. Это выглядит аккуратно в таблице, но превращает историю в подделку.

Второй антипаттерн — хранить только `is_current` без временных интервалов. Флаг текущей записи удобен, но он отвечает только на один вопрос: какая версия действует сейчас. Он не отвечает, когда она начала действовать, когда закончилась предыдущая, какая версия была активна на дату события. Без `valid_from` и `valid_to` история превращается в очередь состояний без календаря. Записи вроде бы есть, но время между ними размыто, и point-in-time-запрос начинает работать на догадках.

Третий антипаттерн — смешивать дату загрузки и дату действия. `Load_dt` говорит, когда данные вошли в хранилище. Дата действия говорит, когда изменение стало верным в бизнес-реальности. Иногда они совпадают, но часто расходятся: источник опоздал, исправление пришло задним числом, файл был загружен ночью, событие произошло днём раньше. Если использовать дату загрузки как дату действия по привычке, система будет строить историю не так, как жил бизнес, а так, как работал технический процесс.

Четвёртая ошибка — не проверять пересечения версий. У одного объекта в один момент времени не должно быть двух одновременно действующих статусов, двух сегментов, двух паспортов одного типа или двух конфликтующих ставок, если бизнес-смысл этого не допускает. Пересечения делают историю двусмысленной: запрос на дату может вернуть две версии, и каждая будет претендовать на правду. Не лучше и случайные дыры, когда на часть периода не действует ни одна версия. Такая история похожа на летопись, где несколько месяцев выпали из календаря, а потом внезапно появились два июля подряд.

Пятый антипаттерн — историзировать всё подряд без бизнес-смысла. Это кажется безопасным: сохраним всё, потом разберёмся. Но бесконтрольная историзация раздувает модель, усложняет загрузку, замедляет запросы и делает важную память неотличимой от шума. История должна быть связана с вопросами, рисками, отчётностью, юридической значимостью, возможностью расследования. Если старое значение никому никогда не нужно и ничего не объясняет, возможно, его не стоит превращать в отдельную историческую драму.

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

7.10. Итог: история должна быть пригодна для вопроса

Хорошая историзация позволяет вернуться в выбранный момент и увидеть данные такими, какими они были нужны для ответа тогда. Не просто открыть старую таблицу. Не просто найти последнюю запись до даты. А восстановить состояние объекта, связи или показателя так, чтобы отчёт мог опереться на него без скрытой подмены настоящим. Это и есть главный смысл истории в КХД: она должна работать, а не лежать.

Поэтому историзация хранит не только значения, но и границы их действия. Значение без периода похоже на фразу без времени: вроде понятно, что сказано, но непонятно, когда это было правдой. `Valid_from`, `valid_to`, признак текущей версии, дата события, дата загрузки, источник изменения — всё это не украшения схемы. Это координаты памяти. Благодаря им система может отличить “сейчас”, “тогда”, “узнали позже”, “исправили задним числом” и “пересчитали после уточнения”.

Прошлое в хранилище не должно быть музейным экспонатом. Музейное прошлое красиво лежит под стеклом, но не участвует в работе. Историческое прошлое КХД должно участвовать: соединяться с фактами, выбирать правильную версию, объяснять расхождения, поддерживать пересчёт, помогать расследовать ошибки. Если история не может ответить на вопрос, она превращается в склад старых строк. Если может — она становится одним из главных механизмов доверия.

КХД без истории помнит только настоящее. А настоящее слишком склонно выдавать себя за вечность. Сегодняшний сегмент клиента начинает казаться его вечным сегментом. Текущий статус договора — состоянием, которое будто бы было всегда. Последняя ставка продукта — ставкой всех прошлых сделок. И тогда система начинает врать про вчера не потому, что у неё плохие данные, а потому что она не оставила вчерашнему дню права существовать. Историзация возвращает это право. Она говорит: было иначе, потом изменилось, и мы можем показать, когда именно.
;
Глава 8

8.1. Качество данных — не чистота, а пригодность

Качество данных часто представляют как чистоту: чтобы не было пустых полей, странных дат, дублей, битых кодов, лишних пробелов и значений, похожих на следы ночного дежурства уставшего оператора. Всё это важно, но это ещё не суть. `DQ` (`Data Quality` — качество данных) начинается не там, где данные выглядят аккуратно, а там, где ими можно пользоваться без скрытого обмана. Данные могут быть ровными, заполненными, красиво приведёнными к формату — и всё равно плохими, если они отвечают не на тот вопрос, относятся не к той дате, взяты не из того источника или потеряли важный след происхождения.

Красивые данные иногда опаснее грязных. Грязная строка хотя бы вызывает подозрение: пустой паспорт, дата рождения в будущем, сумма кредита со знаком минус, статус “не знаю”. Такая строка стучит ложкой по батарее и требует внимания. А красивая ошибка проходит тихо. Клиент аккуратно заполнен, договор имеет статус, сумма выглядит правдоподобно, дата находится в допустимом диапазоне. Только сегмент взят текущий вместо исторического, ставка подтянута из нового справочника, клиент ошибочно склеен с другим, а согласие действует не на тот период. Внешне всё хорошо. Смыслово — яд уже растворился.

Качество всегда зависит от задачи. Для маркетинга устаревший телефон — серьёзная проблема, потому что коммуникация не дойдёт. Для регуляторной отчётности тот же телефон может быть второстепенным, зато критичной станет дата договора, сумма, статус, паспорт, признак резидентства. Для риск-модели важны полнота признаков, корректность рейтинга, история просрочки. Для управленческого отчёта важны согласованность показателей между витринами и воспроизводимость результата. Нет абстрактно “качественных данных” для всего на свете. Есть данные, пригодные или непригодные для конкретного использования.

Поэтому ошибка в данных почти никогда не остаётся только ошибкой в данных. Она становится ошибкой в решении. Неверный статус договора меняет портфель. Неверный сегмент меняет аналитику продаж. Неверная дата согласия меняет юридический риск. Неверный рейтинг меняет отношение к клиенту. Ошибка может пройти через слои, стать витриной, отчётом, KPI, решением комитета, письмом клиенту, регуляторной строкой. И чем дальше она ушла, тем меньше она похожа на техническую мелочь и тем больше — на уже совершённое действие.

Качество данных — это способность хранилища сомневаться в себе до того, как его сомнения станут чужими последствиями. Не обещать идеальную чистоту. Не превращать каждую строку в музейный экспонат. А уметь сказать: здесь значение пригодно, здесь подозрительно, здесь нарушено правило, здесь источник опоздал, здесь расхождение допустимое, здесь нужно остановиться. Хорошее КХД не верит данным слепо. Оно умеет проверять, где именно вера превращается в риск.

8.2. Измерения качества: как назвать боль

Чтобы управлять качеством, нужно уметь назвать дефект. Пока проблема звучит как “данные какие-то плохие”, с ней трудно работать. Это похоже на жалобу “мне нехорошо”: понятно, что что-то не так, но непонятно, где болит и что измерять. Измерения качества нужны не ради красивой классификации, а ради языка, на котором команда может точно сказать: у нас не хватает данных, данные невалидны, данные противоречат друг другу, данные опоздали, данные повторились или данные не похожи на реальность.

`Completeness` — полнота. Она отвечает на вопрос, хватает ли значений там, где они нужны. Пустой телефон может быть терпимым для одного отчёта и критичным для обзвона. Отсутствующий паспорт может остановить KYC-процесс. Пустая дата закрытия может быть нормальной для активного договора и ошибкой для закрытого. Полнота всегда связана с контекстом: поле не обязано быть заполнено вообще всегда, но оно должно быть заполнено тогда, когда без него смысл ломается.

`Validity` — соответствие формату и допустимому домену. Дата должна быть датой, сумма — числом, статус — одним из разрешённых значений, валюта — существующим кодом, email — хотя бы похожим на email. Validity ловит дефекты формы: значение может быть заполнено, но не принадлежать языку системы. Это уровень, на котором данные ещё не обязательно проверяются на правду, но уже проверяются на способность быть прочитанными.

`Uniqueness` — отсутствие лишних повторов. Если один и тот же договор появляется дважды там, где должен быть один, показатели начинают удваиваться. Если клиент повторяется из-за разных ID, система может считать одного человека несколькими. Но уникальность не означает слепую борьбу с каждым повтором. Иногда повтор — это новое событие, новая версия, новая роль. Поэтому проверка уникальности всегда должна знать гранулярность: что именно должно быть уникальным и на каком уровне.

`Consistency` — согласованность. Она смотрит, не спорят ли данные между собой. Договор закрыт, но по нему активная просрочка. Счёт заблокирован, но по нему проходят операции. В core одна сумма, в mart другая. В источнике статус один, в хранилище другой без объяснимого правила. Consistency важна потому, что многие ошибки не видны в одном поле. Каждое значение отдельно может выглядеть допустимым, но вместе они образуют невозможную картину.

`Accuracy` — близость к реальности. Это самое трудное измерение, потому что реальность не всегда лежит рядом в виде эталонной таблицы. Дата рождения 2099 года явно плохая, но неверный адрес или неправильный доход клиента не всегда можно обнаружить простым SQL. Accuracy часто требует внешнего источника, сверки, бизнес-экспертизы, расследования. Это зона, где качество данных выходит за пределы формата и спрашивает: соответствует ли запись тому, что действительно произошло или существует.

`Timeliness` — своевременность. Данные могут быть полными, валидными, уникальными, согласованными и даже точными, но прийти слишком поздно. Для отчёта к восьми утра данные, пришедшие в десять, уже плохие. Для риск-мониторинга задержка в день может быть критичной. Для месячной аналитики — несущественной. Timeliness напоминает: качество связано не только с содержанием, но и с ритмом. Истина, пришедшая после решения, часто уже не спасает.

Эти измерения не нужно воспринимать как школьный список для заучивания. Это разные способы увидеть боль. Полнота говорит: чего-то не хватает. Валидность говорит: значение не на языке системы. Уникальность говорит: что-то повторилось не по праву. Согласованность говорит: части мира спорят между собой. Точность говорит: запись разошлась с реальностью. Своевременность говорит: данные пришли не тогда, когда были нужны. Чем точнее названа боль, тем меньше соблазн лечить все дефекты одной и той же таблеткой.

8.3. Правила качества: технические, бизнесовые, сверочные

Правило качества — это не просто условие в запросе. Это формализованное сомнение системы. Оно говорит: в этом месте мы считаем такое состояние нормальным, а такое — подозрительным или недопустимым. Если правило написано без смысла, оно быстро превращается в шум. Если написано без владельца, на него некому реагировать. Если написано без заранее определённой реакции, оно становится декоративной лампочкой: горит красным, все видят, никто ничего не делает.

Технические проверки смотрят на форму данных. Типы, `NULL`, формат, диапазон, длина строки, допустимые символы, корректность даты, наличие обязательного ключа. Они отвечают на вопрос: можно ли вообще безопасно обработать это значение? Например, сумма не должна быть текстом, дата не должна быть “31 февраля”, статус не должен быть случайной фразой, идентификатор договора не должен быть пустым там, где без него строка теряет смысл. Эти проверки похожи на контроль входа: они не доказывают истину, но не позволяют очевидной поломке идти дальше с видом законного участника процесса.

Бизнес-проверки смотрят глубже. Они знают не только формат, но и смысл. Кредит не может иметь отрицательную сумму выдачи. Дата закрытия не может быть раньше даты открытия, если речь не идёт о специальной корректировке. Закрытый договор не должен иметь активный статус. Клиент младше определённого возраста не может быть заёмщиком по такому продукту. Просрочка должна согласовываться с графиком и фактическими платежами. Такие правила уже требуют участия предметной области. Их нельзя надёжно придумать только из схемы таблицы.

Сверочные проверки сравнивают части системы между собой. Сколько строк пришло из источника и сколько дошло до staging. Сколько договоров принято в core. Сошлась ли сумма платежей после преобразования. Не изменилась ли контрольная сумма. Совпадает ли портфель в mart с агрегатом из core в допустимых границах. Такие проверки особенно важны, потому что они ловят не дефект отдельной строки, а потерю или искажение на пути. Данные могли быть валидными, но часть не доехала. Могли быть полными, но при JOIN размножились. Могли быть правильными в core, но витрина пересчитала их иначе.

У каждого правила качества должен быть смысл, владелец и реакция. Смысл отвечает, зачем правило существует и какой риск оно закрывает. Владелец отвечает, кто понимает это правило и принимает решение при нарушении. Реакция отвечает, что делать: остановить процесс, отправить строки в карантин, предупредить, создать инцидент, принять исключение, пересчитать данные, запросить источник. Без этих трёх вещей контроль качества превращается в коллекцию сигналов, которые постепенно перестают слушать.

Хороший набор правил не стремится проверить всё подряд. Он строится вокруг риска и ответственности. Где ошибка приведёт к неверному отчёту, там правило должно быть строгим. Где ошибка влияет на регуляторику или клиента, там должна быть явная реакция. Где дефект терпим и не ломает смысл, его можно фиксировать мягче. Качество данных — это не тотальная подозрительность ко всему живому. Это умение сомневаться ровно там, где доверие стоит дорого.

8.4. Критичные элементы данных

Не все поля одинаково важны. Это звучит очевидно, но именно здесь многие системы качества начинают расползаться. Команда пытается проверять всё подряд с одинаковой серьёзностью: каждую колонку, каждый формат, каждое редкое отклонение, каждую мелкую странность. В результате появляется много шума, много алертов, много красных лампочек, и очень быстро люди перестают понимать, где действительно опасность, а где просто очередная пылинка на приборной панели.

Для этого вводят понятие `CDE` (`Critical Data Element` — критичный элемент данных). CDE — это не просто важное поле. Это поле, ошибка в котором может повлиять на отчётность, регуляторные обязательства, риск, клиента, деньги, юридическую позицию или управленческое решение. В банковском контексте такими элементами часто становятся паспорт, идентификатор клиента, номер договора, сумма операции, дата операции, статус договора, согласие клиента, риск-рейтинг, признак просрочки, валюта, счёт, контрагент, ставка. Ошибка здесь не остаётся локальной. Она начинает жить дальше и менять смысл процессов вокруг себя.

CDE помогают строить качество от риска, а не от желания быть идеальными. Если поле “комментарий оператора” заполнено не по стандарту, это может быть неприятно, но не всегда критично. Если неверна дата согласия клиента, банк может не иметь права на действие, которое уже совершил. Если сумма операции искажена, агрегаты начнут врать. Если статус договора неверен, портфель, просрочка, резервы и отчёты могут пойти в разные стороны. Критичность поля определяется не его красотой и не удобством проверки, а последствиями ошибки.

Это не значит, что остальные данные можно бросить в темноте. Но глубина контроля должна быть разной. Для CDE нужны строгие правила, понятные владельцы, история изменений, сверки, реакция на нарушения. Для менее критичных полей могут быть мягкие проверки, статистическое наблюдение, периодическая очистка. Хорошая система качества не пытается превратить всё хранилище в стерильную операционную. Она знает, где у данных сердце, где нерв, где кость, а где просто складка на одежде.

Поэтому каталог критичных элементов — один из самых практичных инструментов DQ. Он помогает договориться, какие поля защищаются особенно внимательно, почему они критичны, кто за них отвечает, какие проверки применяются, где они используются и какой уровень дефекта допустим. Без этого качество превращается в абстрактную заботу обо всём сразу. А забота обо всём сразу очень быстро становится заботой ни о чём.

8.5. Качество по слоям КХД

Качество данных проверяется не в одном месте. Оно меняет смысл в зависимости от слоя. В staging проверка не должна пытаться сделать данные красивыми. Здесь важно зафиксировать, что именно пришло: сколько строк, из какого источника, в какой партии, в каком формате, с какими очевидными дефектами. Staging не обязан исправлять реальность. Он обязан сохранить свидетельство и дать системе понять, можно ли это свидетельство безопасно читать дальше.

В core проверка становится строже, потому что здесь данные перестают быть только свидетельством источника и становятся согласованной правдой КХД. Здесь уже нельзя спокойно принять клиента без идентичности, договор без ключа, статус вне допустимого жизненного цикла, связь, которая нарушает модель, или изменение, которое ломает историю. Ошибка в staging ещё может быть шумом источника. Ошибка в core — это уже ошибка принятого решения. Она начинает распространяться во все витрины, отчёты и расчёты.

В mart проверка защищает ответ пользователю. Здесь важно убедиться, что витрина не потеряла строки, не размножила факты, не пересчитала показатель иначе, чем core, не устарела, не нарушила гранулярность. Mart может быть упрощённым, быстрым, денормализованным, но именно поэтому его нужно проверять особенно внимательно: любое удобство несёт риск скрытого искажения. Пользователь редко видит глубину core. Он видит витрину. И если витрина ошиблась, для него ошиблось всё хранилище.

Одна и та же ошибка имеет разную цену на разных слоях. Пустой статус в staging может означать “источник прислал странную строку, надо проверить”. Пустой статус в core может означать “система не смогла принять решение, но всё равно пустила объект дальше”. Пустой статус в mart может означать “отчёт уже показывает неполную реальность бизнесу”. Чем дальше дефект прошёл, тем дороже его объяснять. Сначала это была строка. Потом правило. Потом показатель. Потом решение.

Поэтому качество по слоям — это не повторение одних и тех же проверок в трёх местах. Это система разных вопросов. На входе: что пришло и можно ли это принять к обработке? В core: согласуется ли это с моделью, историей и правилами правды? В mart: можно ли этому ответу доверять как бизнес-форме? Если эти вопросы разделены, контроль качества становится частью архитектуры, а не отдельным уборщиком, который приходит слишком поздно и печально смотрит на уже разлитую краску.

8.6. Что делать с ошибкой

Найти ошибку — только половина дела. Иногда даже меньшая половина. Главный вопрос начинается после обнаружения: что теперь делать? Остановить загрузку? Пропустить строку? Отправить её в карантин? Принять как известное исключение? Поднять инцидент? Предупредить владельца источника? Пересчитать витрину? Если ответ каждый раз придумывается в момент пожара, контроль качества превращается в эмоциональную службу реагирования: кто громче кричит, тот и определяет архитектуру.

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

Пропустить с предупреждением можно там, где дефект не разрушает смысл целиком, но должен быть видимым. Например, часть некритичных полей не заполнена, есть небольшое отклонение по объёму, отдельные значения не прошли мягкое правило. Такие случаи не всегда требуют остановки процесса, но требуют записи: что произошло, сколько строк затронуто, кто уведомлён, когда проблема должна быть разобрана. Предупреждение без последующего внимания быстро становится вежливой формой игнорирования.

Карантин нужен для данных, которые нельзя принять в основную модель, но нельзя и просто выбросить. Строка может быть плохой по формату, нарушать связь, не иметь обязательного ключа, конфликтовать со справочником. Если её удалить, источник и команда потеряют материал для расследования. Если пустить дальше, она заразит модель. Карантин даёт ей отдельную судьбу: она не участвует в расчётах, но сохраняется вместе с причиной отклонения и может быть исправлена, переобработана или возвращена источнику как доказательство проблемы.

Иногда дефект принимают как известное исключение. Это не значит “закрыть глаза”. Это значит: мы понимаем нарушение, знаем его причину, ограничили его влияние и явно согласовали, что временно живём с ним. Например, источник ещё месяц будет присылать старый код статуса, который уже не используется в новой модели. Или часть исторических записей не имеет поля, появившегося только после определённой даты. Исключение должно иметь срок, владельца и объяснение. Иначе оно превращается в постоянную дырку, вокруг которой все давно научились ходить молча.

Реакция на дефект должна быть заранее определена. У каждого правила качества должен быть уровень критичности и сценарий действия. Тогда система не паникует, когда встречает плохие данные. Она делает то, что было решено: останавливается, предупреждает, изолирует, принимает исключение, создаёт задачу, ждёт исправления. В этом и есть зрелость DQ: не в том, что ошибок нет, а в том, что у ошибки есть маршрут, а не только звук сирены.


8.7. Качество тоже должно храниться как данные

Если качество данных проверяется, но результаты проверок нигде нормально не живут, система остаётся почти слепой. Она может заметить конкретный сбой сегодня, но не видит собственного состояния во времени. Было ли хуже вчера? Улучшается ли источник? Растёт ли доля ошибок? Какое правило падает чаще всего? Кто отвечает за проблему? Когда она впервые появилась? Без памяти о проверках DQ превращается в набор одноразовых криков.

Поэтому качество тоже должно храниться как данные. Нужен реестр проверок: какое правило существует, к какому слою относится, какую таблицу или поле проверяет, кто владелец, какой уровень критичности, что считается ошибкой и что система должна сделать при нарушении. Рядом должны жить результаты запусков: дата проверки, статус, количество ошибок, доля ошибок, примеры строк, ссылка на партию загрузки, сообщение об ошибке, решение или комментарий. Это уже не просто контроль. Это биография доверия.

Особенно важна история DQ-результатов. Разовый сбой заметить легко: вчера было ноль ошибок, сегодня тысяча. Гораздо опаснее медленное ухудшение, когда доля пустых значений растёт понемногу, количество дублей увеличивается каждую неделю, источник начинает опаздывать всё чаще, но ни один день не выглядит достаточно страшным для паники. История качества позволяет увидеть не только аварию, но и болезнь до аварии.

Когда результаты проверок хранятся как полноценные данные, ими можно управлять: строить витрины качества, смотреть тренды, сравнивать источники, оценивать критичность, доказывать, что проблема пришла извне или появилась внутри КХД. Система перестаёт просто говорить “плохо” и начинает показывать: где плохо, как давно, насколько сильно, кто должен ответить и что уже было сделано.

8.8. Сверки и метрики доверия

Сверка начинается с простого вопроса: дошло ли до нас то, что должно было дойти? Количество строк — самый грубый, но очень полезный сигнал. Если источник обычно присылает миллион операций, а сегодня пришло сто тысяч, это ещё не доказывает ошибку, но требует объяснения. Если после загрузки в core строк стало больше, чем в staging, нужно понять, это законное размножение из-за связей или случайный эффект плохого соединения. Если в mart исчезла часть договоров, отчёт может выглядеть спокойным только потому, что потерял проблему.

Контрольные суммы и суммы по ключевым денежным полям помогают увидеть искажение там, где простое количество строк молчит. Строк могло прийти столько же, но суммы изменились. Платежи могли не потеряться, но задвоиться. Баланс мог пройти все технические проверки, но не сойтись с источником. В банковских данных денежные поля требуют особой осторожности: одна неверно умноженная сумма способна выглядеть как успешная загрузка до тех пор, пока её не увидит отчётность.

Сверки между source, core и mart показывают, где именно смысл начал расходиться. Source говорит: вот что я отправил. Core говорит: вот что я принял и согласовал. Mart говорит: вот что я подготовил для ответа. Между этими слоями могут быть законные отличия, потому что данные очищаются, фильтруются, агрегируются, историзируются. Но каждое отличие должно быть объяснимым. Если разница есть, а правила её происхождения нет, доверие начинает вытекать через трещину.

Метрики вроде freshness, volume anomaly и latency здесь важны не как повтор мониторинга pipeline, а как признаки доверия. Freshness показывает, насколько данные свежие. Volume anomaly — не выглядит ли объём данных необычно. Latency — сколько времени прошло между событием, поступлением и доступностью для использования. Эти метрики не говорят всей правды о качестве, но показывают, можно ли вообще считать данные своевременными и похожими на ожидаемую поставку.

Главная мысль простая: “данные загрузились” ещё не значит “данным можно верить”. Загрузка говорит о факте движения. Сверка говорит о сохранности смысла в этом движении. Хорошее КХД не удовлетворяется тем, что процесс завершился зелёным статусом. Оно спрашивает: столько ли пришло, столько ли принято, столько ли опубликовано, не изменилась ли сумма, не размножились ли факты, не потерялась ли важная часть мира по дороге.

8.9. Банковские сценарии качества

Договор без клиента — один из самых понятных сигналов беды. Сам по себе номер договора может быть валидным, сумма может быть заполнена, статус может выглядеть допустимым. Но если договор не связан с клиентом, банковская реальность становится неполной: непонятно, кто несёт обязательство, чей риск учитывать, кому принадлежит история, в какой сегмент попадёт портфель. Такая ошибка ломает не поле, а связь.

Платёж без счёта похож на движение без места. Деньги будто бы прошли, но непонятно где. Для отчёта это может стать потерянной суммой, для сверки — расхождением, для расследования — тупиком. Отрицательная сумма кредита, если она не является специальной корректировкой, говорит уже о нарушении бизнес-смысла. Формат может быть правильным, тип данных тоже, но значение противоречит природе события.

Закрытый договор с активной просрочкой показывает конфликт состояний. Возможно, договор закрыли ошибочно. Возможно, просрочку не сняли. Возможно, модель неправильно понимает жизненный цикл договора. В любом случае проблема не находится в одной колонке. Она рождается между полями, и именно поэтому такие проверки особенно ценны: они ловят не грязь на поверхности, а внутреннее противоречие.

Согласие клиента без даты опасно юридически. Сам факт согласия ещё недостаточен, если нельзя понять, когда оно было дано, на что распространялось и действовало ли в момент использования. Риск-рейтинг без источника опасен аналитически: число есть, но непонятно, кто его породил, по какой модели, на какую дату и можно ли ему доверять. Баланс, который не сходится с контрольной суммой, опасен финансово: он может быть красивым в витрине, но не подтверждённым системой, откуда пришла исходная реальность.

Такие сценарии важны не как набор страшных примеров, а как способ мышления. В банке качество данных почти всегда связано с последствиями: кто клиент, где деньги, действует ли право, верен ли риск, сходится ли баланс, можно ли доказать состояние на дату. Хорошая DQ-система проверяет не только форму строки, но и способность этой строки выдержать вопрос бизнеса.

8.10. Антипаттерны Data Quality

Первый антипаттерн — проверять качество только в конце. Данные уже прошли источник, staging, core, трансформации, витрины, а потом кто-то смотрит на отчёт и говорит: “число странное”. В этот момент ошибка уже успела переодеться несколько раз. Она могла быть пропуском в источнике, дублем при загрузке, неверным сопоставлением в core, размножением при JOIN, ошибкой агрегации в mart. Чем позже система замечает дефект, тем меньше он похож на конкретную строку и тем больше — на туман, который нужно расследовать с фонарём и плохим настроением.

Второй антипаттерн — проверки без владельца. Правило есть, результат есть, ошибка есть, но никто не отвечает за смысл. Техническая команда говорит: “мы только проверяем”. Бизнес говорит: “мы не знаем, что там в таблицах”. Источник говорит: “у нас всё нормально”. Так рождается бесхозная ошибка: она появляется, фиксируется, висит в отчёте качества, стареет, покрывается комментариями и постепенно становится частью пейзажа. Проверка без владельца почти так же бесполезна, как пожарная сигнализация в здании, где никто не знает, кто должен выйти из комнаты.

Третий антипаттерн — считать все ошибки одинаковыми. Пустой комментарий оператора, отсутствующий паспорт, невалидный статус договора, опоздавшая поставка, расхождение в денежной сумме и дубль клиента не могут иметь один и тот же вес. Если всё критично, то ничего не критично. Люди перестают различать опасность, потому что система всё время кричит одним голосом. Качество данных требует градации: где можно предупредить, где нужно изолировать, где надо остановить процесс, где допустимо временное исключение.

Четвёртый антипаттерн — алерты, на которые никто не реагирует. Сначала они тревожат, потом раздражают, потом превращаются в фоновый шум. Самый плохой алерт не тот, который сработал зря один раз, а тот, который годами сообщает о реальной проблеме, но все уже привыкли его игнорировать. Такая система не повышает доверие, а разрушает его: она учит людей, что красный цвет ничего не значит.

Пятый антипаттерн — ручные исправления без следа. Кто-то поправил значение прямо в таблице, чтобы отчёт сошёлся. Кто-то удалил дубль, потому что “мешал”. Кто-то поменял статус, потому что бизнес попросил срочно. Иногда это действительно спасает день. Но если у исправления нет причины, автора, времени, старого значения, нового значения и связи с инцидентом, то система заплатила за быстрый успех потерей памяти. Завтра никто не поймёт, почему данные такие, а послезавтра эту правку перезатрёт загрузка или воспроизведёт старую ошибку.

Шестой антипаттерн — правила качества живут в отчётах, а не в КХД. Аналитик в одном отчёте фильтрует странные статусы, другой исключает подозрительные суммы, третий вручную убирает клиентов без договора. Снаружи кажется, что отчёт стал чище. Внутри система стала слабее, потому что правила контроля расползлись по частным местам. Ошибка не исчезла, она просто была вежливо спрятана от одного пользователя и осталась жить для всех остальных.

И наконец, самый общий антипаттерн: качество проверяется, но не управляется. Есть запросы, дашборды, статусы, проценты ошибок, но нет процесса принятия решений. Никто не пересматривает правила, не закрывает старые исключения, не разговаривает с источниками, не меняет пороги, не оценивает риск. Такая DQ-система похожа на врача, который каждый день меряет температуру, аккуратно записывает жар и ни разу не назначает лечение.

8.11. Итог: доверие должно быть измеримым

Качество данных — это не вера в систему. Вера начинается там, где кончается проверка, а хранилище не должно требовать веры как религиозного жеста. Оно должно уметь показать свои сомнения: какие проверки выполнены, где есть нарушения, какие поля критичны, какие источники нестабильны, где расхождения объяснены, а где ещё нет. Доверие к данным рождается не из обещания “у нас всё хорошо”, а из возможности увидеть, почему мы считаем это достаточно хорошим.

Хорошее КХД не обещает, что ошибок не будет. Это было бы детской сказкой, причём написанной человеком, который никогда не подключал банковский источник. Ошибки будут: источники опоздают, форматы изменятся, строки задвоятся, справочники разъедутся, бизнес-правила устареют, люди нажмут не ту кнопку, системы пришлют старую правду в новом файле. Зрелость не в отсутствии ошибок, а в том, что ошибка не проходит сквозь систему невидимкой.

Поэтому ошибка должна стать видимой, измеримой и объяснимой. Видимой — значит система замечает её и показывает нужным людям. Измеримой — значит можно понять масштаб: сколько строк, какие суммы, какой период, какие источники, какие критичные поля. Объяснимой — значит можно восстановить путь: где дефект возник, какое правило сработало, почему реакция была именно такой, что сделано дальше. Только тогда ошибка перестаёт быть мистическим событием и становится управляемым фактом.

Данным можно доверять только тогда, когда известно, как именно это доверие проверяется. Не вообще, не “у нас есть DQ”, не “мы смотрим на отчёты”, а конкретно: какие правила защищают какие элементы, где хранятся результаты, кто владеет дефектами, какие пороги считаются допустимыми, что происходит при нарушении. Доверие без измерения — это надежда. А надежда, конечно, прекрасна в поэзии, но в корпоративном хранилище данных ей лучше не выдавать права администратора.


Рецензии