# Контракт события в 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. `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`, `dm.top_pages_daily_v`, `dm.utm_effectiveness_v` (`declared_revenue` — из `purchase_revenue`), `dm.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) — «Нормализованное имя», «Хранилище».