- Зачем:
- GitHub-аккаунт заблокирован 2026-07-26; трекер карты «боевой
реализм» перенесён на git.dementev.space, контекст нужен новой
сессии независимо от исхода апелляции.
- Что:
- .scratch/backup/ — слепок всех 27 issues и резолюция тикета #15
на момент блокировки;
- .scratch/handoffs/ — handoff с состоянием карты, нюансами доступа
к Gitea и списком хвостов (апелляция, выбор основного трекера).
- Проверка:
- тексты читаются; ссылки на Gitea-трекер открываются
(git.dementev.space/ddmitry/clickstream-ch-kafka-superset-demo).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
100 lines
8.1 KiB
Markdown
100 lines
8.1 KiB
Markdown
## Ответ
|
||
|
||
Сумма заказа живёт **на обеих сторонах**, и стороны законно расходятся:
|
||
клиентское событие несёт объявленную сумму (то, что сайт продиктовал трекеру
|
||
через dataLayer — до пересчёта на бэкенде), заказ бэкенда — итоговую правду
|
||
со статусами. Правило стенда: **поведение и атрибуцию считаем по трекеру,
|
||
деньги — по бэкенду**. Правило выучивается на конфликте: суммы у менти
|
||
не сойдутся, и он сам раскопает почему.
|
||
|
||
### Клиентская сторона (широкое событие, рамка #18)
|
||
|
||
- Таксономия торговых событий: `pageview` + `add_to_cart` + `purchase`.
|
||
Полный словарь Метрики (`detail`, `remove`, `impressions`) не берём:
|
||
механика та же, объём массивов большой, новых идей нет — остаётся теорией
|
||
в документе о реализме.
|
||
- `purchase` несёт `purchaseID/Revenue/Currency/Coupon` + массивы `product*`
|
||
состава заказа + сырую строку `ecommerce` (по образцу выгрузки Метрики,
|
||
фактура — #27).
|
||
- `add_to_cart` — массивы `product*` с одним товаром. Оживают мёртвые
|
||
счётчики `purchases`/`add_to_carts` в `v_utm_effectiveness` без правки витрины.
|
||
|
||
### Сторона бэкенда (второй источник, рамка #18)
|
||
|
||
- **Формат выгрузки: полный ежедневный слепок заказов в пределах окна
|
||
изменяемости K дней.** Запись = снапшот заказа: `order_id`, `user_id`,
|
||
времена, `status` (`created`/`paid`/`cancelled`), `updated_at`, итоговые
|
||
суммы (товары / скидка / доставка / итого) + **позиции вложенным JSON**
|
||
(`items: [{sku, qty, price}]`) — «родной» экспорт бэкенда, а не диалект
|
||
Метрики.
|
||
- Обоснование слепка (а не инкремента): очень частый боевой формат;
|
||
идемпотентный приём и самовосстановление (пропущенный день ничего не
|
||
ломает); дедуп до последней версии становится обязательным с первого дня.
|
||
Окно K делает объём защитимым (за окном заказ неизменяем — возить незачем)
|
||
и даёт границу пересчёта: выручка дня D «дышит» K дней, потом замерзает.
|
||
- Приём: дедуп через `ReplacingMergeTree`/`argMax`; разбор JSON-позиций —
|
||
**один раз** в трансформации ODS → DDS, дальше витрины работают с плоскими
|
||
массивами. Это единственный носитель навыка «вложенный JSON в ClickHouse»
|
||
в стенде.
|
||
- В `docs/generator-realism.md` — честный абзац trade-off «инкремент экономнее,
|
||
слепок надёжнее» и сноска про compacted topic как родной Kafka-паттерн
|
||
для состояния сущности.
|
||
|
||
### Сверка
|
||
|
||
- **Ключ: клиентский `purchaseID` = `order_id` бэкенда** (магазин знает номер
|
||
заказа на `/confirmation` — как `actionField.id` у Метрики).
|
||
- Конструируемые расхождения — перечислимый список из четырёх причин,
|
||
детерминированных от seed:
|
||
- **A. Отмена** — `purchase` есть, заказ дошёл до `status='cancelled'`
|
||
(бесплатно из статусов);
|
||
- **B. Потерянное событие** — заказ есть, `purchase` не доехал
|
||
(вероятность в генераторе);
|
||
- **C. Дельта суммы** — систематическая, по построению: клиент объявляет
|
||
сумму товаров до промокода и доставки, бэкенд — итог; менти может вывести
|
||
формулу связи;
|
||
- **D. Дубль события** — повторный `purchase` от обновления
|
||
`/confirmation` (вероятность).
|
||
- Пятый эффект бесплатно даёт формат доставки: **опоздание** (заказ впервые
|
||
появляется в слепке D+1/D+2) — «вчера не сходилось, сегодня сошлось».
|
||
- Не берём: сироту-фрод (дублирует B механически) и расхождение часов
|
||
клиент/сервер — это жители тумана «Грязь в данных», придут своим тикетом.
|
||
|
||
### Слои и витрины
|
||
|
||
- DDS: одна новая сущность `dds.order` (последняя версия заказа, позиции
|
||
разобраны в массивы). `purchase` отдельной сущности не получает — это
|
||
строка широкого события.
|
||
- DM, три роли: **выручка** (`v_revenue_daily`, по категориям через `dictGet`
|
||
каталога) — строится только от заказов; **сверка**
|
||
(`v_purchase_vs_orders`) — FULL OUTER JOIN по ключу с классификатором
|
||
расхождения; **атрибуция** (`v_utm_effectiveness`) — остаётся клиентской,
|
||
плюс объявленная выручка по UTM. Точный состав колонок — в спеку (#17).
|
||
|
||
### Эталонный мир
|
||
|
||
Пересборка артефакта неизбежна и уже оплачена решением #18; этот тикет
|
||
нагружает её смыслом: манифест расширяется контрольными числами заказной
|
||
стороны — заказы и выручка по дням, и ровно N потерянных / M дублей /
|
||
K отмен для самопроверки лабы сверки. Политика версионирования артефакта
|
||
здесь не решается (пункт тумана карты).
|
||
|
||
### Ограничения исполнения и страховочные срезы
|
||
|
||
Аудитория — джун после базовой программы: расхождения — перечислимый список,
|
||
не хаос. Если при сборке спеки (#17) суммарный объём испугает, резать в
|
||
порядке: (1) статусы сузить до `created`/`cancelled` — урок дедупа держится
|
||
на самом слепке; (2) расхождения вводить поэтапно — сначала A+C, потом B+D;
|
||
(3) окно K сделать константой мира, а не параметром.
|
||
|
||
### Отклонено по дороге
|
||
|
||
Событийный лог заказов (сборка автомата — дальше от типовой работы DE),
|
||
CDC-формат (имитация Debezium без Debezium), шапка+строки (воскрешает склейку
|
||
по ключам), отдельный поток возвратов, полный словарь торговых событий
|
||
Метрики, механика Sign/CollapsingMergeTree (остаётся кандидатом на потом,
|
||
её дом — клиентская сторона, поток визитов Метрики Про, не заказы).
|
||
|
||
Фактура: `docs/research/2026-07-26-yandex-clickstream-format.md` (#27),
|
||
резолюция «Модель данных» (#18).
|