docs(dds): спроектирован контракт dds.event_v — язык модели поверх ODS
- Зачем: - развилка #132 карты #127: витринам DM и сборке сессий нужен контракт события на языке бизнес-модели, первый пример правила «слой — это контракт, а не обязательно копия данных». - Что: - создан docs/architecture/dds/event.md: представление над ods.event_v, имена модели (event_id, event_time в поясе счётчика, event_date переименованием), расшифровка device_category, скрыты VisitID, Sign, ecommerce и служебные колонки. - в README карта модели ведёт на event.md, развилка убрана из открытых решений; session.md ссылается на резолюцию судьбы VisitID. - Проверка: - вычитка; относительные ссылки в docs/architecture/dds/ ведут на существующие файлы. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
83f4e0952b
commit
92ed3011e6
3 files changed
+139
-8
No files matched your search
@@ -24,8 +24,8 @@
|
|||||||
- **Сессия** (`dds.session`) — расчётная сущность: одна строка — визит,
|
- **Сессия** (`dds.session`) — расчётная сущность: одна строка — визит,
|
||||||
нарезку по правилу визита стенд делает сам, `VisitID` генератора — эталон
|
нарезку по правилу визита стенд делает сам, `VisitID` генератора — эталон
|
||||||
сверки; наполнение заменой дневной партиции — [модель сессии](session.md).
|
сверки; наполнение заменой дневной партиции — [модель сессии](session.md).
|
||||||
- **Событие** (`dds.event_v`) — контракт над ODS, а не копия: язык
|
- **Событие** (`dds.event_v`) — контракт над ODS, а не копия: представление,
|
||||||
бизнес-модели событию даёт развилка #132.
|
переводящее событие на язык бизнес-модели, — [контракт события](event.md).
|
||||||
- **Карта идентичностей** (`dds.identity_map`) — кука ↔ пользователь
|
- **Карта идентичностей** (`dds.identity_map`) — кука ↔ пользователь
|
||||||
магазина; сборку решает развилка #133.
|
магазина; сборку решает развилка #133.
|
||||||
|
|
||||||
@@ -49,8 +49,7 @@
|
|||||||
|
|
||||||
## Открытые решения
|
## Открытые решения
|
||||||
|
|
||||||
- Контракт `dds.event_v`, сборка `dds.identity_map` — развилки #132–#133
|
- Сборка `dds.identity_map` — развилка #133 карты #127.
|
||||||
карты #127.
|
|
||||||
- Запуск трансформаций и механика замены партиций на кластере — #134 и
|
- Запуск трансформаций и механика замены партиций на кластере — #134 и
|
||||||
этап 5.
|
этап 5.
|
||||||
- Витрины DM — #135.
|
- Витрины DM — #135.
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
# Контракт события в DDS
|
||||||
|
|
||||||
|
Учебный результат: менти видит первый на стенде пример правила «слой — это
|
||||||
|
контракт, а не обязательно копия данных». Заказ и сессия — физические
|
||||||
|
таблицы, а событие в DDS не хранится вовсе: `dds.event_v` — представление,
|
||||||
|
которое переводит событие на язык бизнес-модели, не копируя ни строки.
|
||||||
|
Витрины DM читают его, а не ODS напрямую.
|
||||||
|
|
||||||
|
## Форма
|
||||||
|
|
||||||
|
Обычное представление над `ods.event_v` — вторым уровнем, так что `FINAL`
|
||||||
|
остаётся спрятанным в один слой, внутри контракта ODS. Единственное
|
||||||
|
сомнение против такой формы — «представление материализуется до фильтра» —
|
||||||
|
снято исследованием [проталкивания
|
||||||
|
условий](../../research/2026-08-22-view-predicate-pushdown.md): условия
|
||||||
|
внешних запросов доезжают до партиций и первичного ключа сквозь два уровня
|
||||||
|
представлений и переименование колонок.
|
||||||
|
|
||||||
|
Две оговорки исследования соблюдены контрактом. Ключевая для срезов колонка
|
||||||
|
`event_date` отдаётся переименованием `EventDate`, не пересчётом — иначе
|
||||||
|
индекс перестал бы её анализировать. Цена `FINAL` — отключённый
|
||||||
|
автоматический `PREWHERE` — свойство ODS, представление её не усугубляет.
|
||||||
|
|
||||||
|
## Язык модели
|
||||||
|
|
||||||
|
Имена атрибутов — имена модели, а не нормализованные имена источника
|
||||||
|
([CONTEXT.md](../../../CONTEXT.md), «Нормализованное имя»). Для большинства
|
||||||
|
колонок они совпадают: атрибут переезжает под нормализованным именем из
|
||||||
|
[описания выгрузки](../../formats/clickstream-event.md). Расхождений три,
|
||||||
|
и каждое — решение модели:
|
||||||
|
|
||||||
|
- **`WatchID` → `event_id`**: модель зовёт сущность событием, «watch» —
|
||||||
|
жаргон выгрузки Метрики.
|
||||||
|
- **`UTCEventTime` → `event_time` в поясе счётчика** (`Europe/Samara`):
|
||||||
|
DDS обслуживает человека с дашбордом, время лежит местным ([дока
|
||||||
|
хранилища](../storage.md), «Часовые пояса»). Пересчёт — смена линзы, байты
|
||||||
|
те же, проталкиванию не мешает. Второй метки времени контракт не отдаёт:
|
||||||
|
выбор линзы слой сделал за читателя, UTC остаётся языком ODS.
|
||||||
|
- **`EventDate` → `event_date`** — переименованием, см. «Форма».
|
||||||
|
|
||||||
|
`ClientID` остаётся `client_id`: это устоявшееся имя куки во всей
|
||||||
|
экосистеме Метрики, по нему шардирование и мост к `dds.identity_map`;
|
||||||
|
своё имя здесь создало бы третий синоним. `client_timezone` тоже остаётся —
|
||||||
|
это атрибут посетителя, а не линза времени.
|
||||||
|
|
||||||
|
## Расшифровки
|
||||||
|
|
||||||
|
Одна: `device_category` — коды 1–4 в строки `desktop` / `mobile` /
|
||||||
|
`tablet` / `tv`. У настоящей Метрики этот атрибут и есть строка (выгрузка
|
||||||
|
стенда несёт число — принятое отступление, спека 1.1), контракт возвращает
|
||||||
|
ему боевой вид.
|
||||||
|
|
||||||
|
Другие коды остаются кодами. `goals_reached` — справочника целей на стенде
|
||||||
|
нет, id и есть язык. Регионы в расшифровке не нуждаются: выгрузка несёт
|
||||||
|
названия рядом с id (`region_city`, `region_country`), словарь ничего бы не
|
||||||
|
раскрыл. Идея дать менти собрать справочник регионов самому ушла в лабы —
|
||||||
|
issue #143.
|
||||||
|
|
||||||
|
## Что скрыто
|
||||||
|
|
||||||
|
- **`VisitID`** — чужой суррогат сессии: свою сессию модель чеканит сама
|
||||||
|
(`session_id`, [модель сессии](session.md)), а эталон — инструмент
|
||||||
|
проверки, не атрибут модели. DQ-сверка нарезки идёт за эталоном в
|
||||||
|
`ods.event_v`: она по построению сопоставляет наш слой с чужим чеканом,
|
||||||
|
и честно, что за его сторону она ходит в его слой.
|
||||||
|
- **`Sign`** — колонка формата без механики: генератор исправлений не шлёт,
|
||||||
|
и в модели события колонке ответить нечего. Честный рассказ о ней живёт в
|
||||||
|
DDL ODS и лекции.
|
||||||
|
- **`ecommerce`** — сырой JSON: сырьё, а не атрибут. Разобранные массивы
|
||||||
|
`purchase_*` и `product_*` в контракте есть; лаба про разбор JSON внутри
|
||||||
|
колонки читает ODS.
|
||||||
|
- **`_load_id`, `_load_ts`** — у представления нет собственной загрузки, а
|
||||||
|
чужие координаты слой не переносит (правило слоя — [модель
|
||||||
|
заказа](order.md), «Служебные колонки»); расследование спрашивает ODS по
|
||||||
|
ключу события.
|
||||||
|
|
||||||
|
Остальные колонки отдаются целиком, включая `ip_address` и оба
|
||||||
|
`region_*_id`: контракт — не цензор, он прячет только то, чему в модели
|
||||||
|
нечего сказать.
|
||||||
|
|
||||||
|
## Читатели
|
||||||
|
|
||||||
|
- Сборка `dds.session`: ключи нарезки (`client_id`, `event_time`,
|
||||||
|
`event_date`), тип события, страница, UTM, паспорт куки — категория
|
||||||
|
устройства приходит уже расшифрованной.
|
||||||
|
- Витрины `dm.events_enriched_v`, `top_pages_daily_v`,
|
||||||
|
`utm_effectiveness_v` (`declared_revenue` — из `purchase_revenue`),
|
||||||
|
`daily_traffic_v` (пара «посетители / известные» через `identity_map`).
|
||||||
|
- Событийная сторона сверки `dm.purchase_vs_orders_v`: `purchase_id`,
|
||||||
|
`purchase_revenue`, `event_date`.
|
||||||
|
- DQ-сверка сессий с эталоном читает не его, а `ods.event_v` —
|
||||||
|
см. «Что скрыто».
|
||||||
|
|
||||||
|
## Не входит
|
||||||
|
|
||||||
|
- DDL представления и его раскатка на кластер — исполнение этапа 4,
|
||||||
|
нарезка #136.
|
||||||
|
- Форма витрин DM — #135.
|
||||||
|
- Лаба со справочником регионов — issue #143, проектирование лаб.
|
||||||
|
|
||||||
|
## Отклонённые варианты
|
||||||
|
|
||||||
|
- **Материализация вместо представления.** Дублировала бы широкий ODS ради
|
||||||
|
переименований, и урок «слой — контракт, а не копия» пропал бы;
|
||||||
|
единственный довод за — сомнение в проталкивании условий — снят
|
||||||
|
исследованием #128.
|
||||||
|
- **`utc_event_time` второй меткой рядом с `event_time`.** Контракт вернул
|
||||||
|
бы читателю выбор линзы, который слой обязан делать сам; расхождение двух
|
||||||
|
часов — урок ODS, не DDS.
|
||||||
|
- **`visit_id` в контракте.** Сверка и лаба читали бы одну поверхность, но
|
||||||
|
чужой чекан встал бы атрибутом модели — тем же доводом модель сессии не
|
||||||
|
пустила его в свою строку. Решение владельца: DQ-опыт показательнее,
|
||||||
|
когда эталон приходит из другого слоя.
|
||||||
|
- **`cookie_id` вместо `client_id`.** Честное имя модели, но третий синоним
|
||||||
|
куки на ровном месте: проза говорит «кука», экосистема — `ClientID`.
|
||||||
|
- **Словарь регионов на стенде.** Повторил бы урок `dictGet` каталога
|
||||||
|
вторым разом, а раскрывать нечего — названия уже в строке. Идея «сделать
|
||||||
|
руками» ушла в лабы (issue #143).
|
||||||
|
- **Прочие расшифровки** (`goals_reached` в имена целей, `has_gclid` в
|
||||||
|
Bool). Справочника целей нет; косметика без читателя.
|
||||||
|
|
||||||
|
## Связанные решения
|
||||||
|
|
||||||
|
- [Проталкивание условий сквозь
|
||||||
|
представления](../../research/2026-08-22-view-predicate-pushdown.md) —
|
||||||
|
резолюция #128: форма контракта и обе её оговорки.
|
||||||
|
- [Модель сессии](session.md) — `session_id` и довод против чужого чекана;
|
||||||
|
сюда же передан вопрос о судьбе `VisitID`.
|
||||||
|
- [Модель заказа](order.md) — правило служебных колонок слоя.
|
||||||
|
- Мастер-спека, раздел 7 — два контракта: `ods.event_v` хранит язык
|
||||||
|
источника, `dds.event_v` говорит языком модели.
|
||||||
|
- [CONTEXT.md](../../../CONTEXT.md) — «Нормализованное имя», «Хранилище».
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
смотрят через `dds.event_v`; сессия и по смыслу не финальна, пока день не
|
смотрят через `dds.event_v`; сессия и по смыслу не финальна, пока день не
|
||||||
прожит.
|
прожит.
|
||||||
|
|
||||||
Источник сборки — `dds.event_v`; его контракт решает развилка #132, сессии
|
Источник сборки — `dds.event_v` ([контракт события](event.md)): сессии
|
||||||
нужны от него ключи нарезки, тип события, страница, UTM и паспорт куки.
|
нужны от него ключи нарезки, тип события, страница, UTM и паспорт куки.
|
||||||
Механика запуска — где собирается партиция и как подменяется на кластере —
|
Механика запуска — где собирается партиция и как подменяется на кластере —
|
||||||
не входит: это устройство трансформаций (#134, этап 5).
|
не входит: это устройство трансформаций (#134, этап 5).
|
||||||
@@ -95,9 +95,9 @@
|
|||||||
компенсация ошибок внутри одной куки за день; эталон в модель не тащим —
|
компенсация ошибок внутри одной куки за день; эталон в модель не тащим —
|
||||||
строка сессии остаётся нашей, а не генераторной. Поштучное сопоставление
|
строка сессии остаётся нашей, а не генераторной. Поштучное сопоставление
|
||||||
границ — материал лабы, где менти разбирает найденное расхождение руками. Дом проверки —
|
границ — материал лабы, где менти разбирает найденное расхождение руками. Дом проверки —
|
||||||
контур DQ этапа 4. Сверке нужен `VisitID`, а сборке сессий — нет; останется
|
контур DQ этапа 4. Сверке нужен `VisitID`, а сборке сессий — нет; контракт
|
||||||
ли он в контракте `dds.event_v` или проверка пойдёт за ним в ODS — решает
|
`dds.event_v` его скрывает, и за эталоном проверка идёт в `ods.event_v`
|
||||||
развилка #132.
|
([контракт события](event.md)).
|
||||||
|
|
||||||
## Читатели
|
## Читатели
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user