Purchase с выручкой: форма события и место в стенде #15

Closed
opened 2026-07-26 21:39:16 +03:00 by ddmitry · 1 comment
Owner

Part of #10

Question

Решить форму события покупки с суммой заказа: схема и носитель
(клиентское событие в широкой модели, источник заказов бэкенда или
обе стороны со сверкой — зависит от решения «Модель данных» #18),
как ложится в DDL/DM и дашборд, что делает с эталонным миром
(пересборка артефакта и чисел лаб).


Комментарий владельца (перенос с GitHub, dementev-dev): решение #18
добавило вторую сущность: заказ бэкенда (Kafka, пачками, с опозданиями
и отменами). При решении формы purchase решить и форму события заказа,
и сюжет сверки «клиентский purchase против бэкенд-заказа». Фактура по
ecommerce Яндекса — в docs/research/2026-07-26-yandex-clickstream-format.md.

Blocked by: #11, #18 (обе закрыты). Перенесено с GitHub 2026-07-26.

Part of #10 ## Question Решить форму события покупки с суммой заказа: схема и носитель (клиентское событие в широкой модели, источник заказов бэкенда или обе стороны со сверкой — зависит от решения «Модель данных» #18), как ложится в DDL/DM и дашборд, что делает с эталонным миром (пересборка артефакта и чисел лаб). --- *Комментарий владельца (перенос с GitHub, dementev-dev):* решение #18 добавило вторую сущность: заказ бэкенда (Kafka, пачками, с опозданиями и отменами). При решении формы purchase решить и форму события заказа, и сюжет сверки «клиентский purchase против бэкенд-заказа». Фактура по ecommerce Яндекса — в docs/research/2026-07-26-yandex-clickstream-format.md. *Blocked by: #11, #18 (обе закрыты). Перенесено с GitHub 2026-07-26.*
ddmitry added the wayfinder:grilling label 2026-07-26 21:39:16 +03:00
Author
Owner

Резолюция (дословный перенос с GitHub, автор dementev-dev, issues/15#issuecomment-5084445090):

Ответ

Сумма заказа живёт на обеих сторонах, и стороны законно расходятся:
клиентское событие несёт объявленную сумму (то, что сайт продиктовал трекеру
через 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).

*Резолюция (дословный перенос с GitHub, автор dementev-dev, issues/15#issuecomment-5084445090):* ## Ответ Сумма заказа живёт **на обеих сторонах**, и стороны законно расходятся: клиентское событие несёт объявленную сумму (то, что сайт продиктовал трекеру через 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).
ddmitry added a new dependency 2026-07-29 20:39:48 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: ddmitry/clickstream-ch-kafka-superset-demo#15