From 3fa669a92069d8737ab0b1b3cae9c61214ad9861 Mon Sep 17 00:00:00 2001 From: Dmitry Dementev Date: Thu, 3 Sep 2026 04:34:25 -0400 Subject: [PATCH] =?UTF-8?q?docs(handbook):=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=BE=20=D0=BE=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=81=D0=BE=D0=B5=D0=B4=D0=B8=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B9=20=D0=BD=D0=B0=20=D0=BA=D0=B0=D1=80=D1=82?= =?UTF-8?q?=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Зачем: - читатель карты видит ключи переноса данных, не переходя к карточкам узлов. - Что: - для 17 стрелок добавлено поле «Как соединяет», сверенное с SQL. - раздел выведен в карточке стрелки и пересобрана карта. - Проверка: - uv run docs/handbook/architecture-map/render.py --check. - make lint. - make typecheck && make test в generator/. --- docs/handbook/architecture-map.html | 18 ++++++++++++++++++ docs/handbook/architecture-map/map.yaml | 17 +++++++++++++++++ docs/handbook/architecture-map/template.html | 1 + 3 files changed, 36 insertions(+) diff --git a/docs/handbook/architecture-map.html b/docs/handbook/architecture-map.html index c493bc9..893016f 100644 --- a/docs/handbook/architecture-map.html +++ b/docs/handbook/architecture-map.html @@ -826,6 +826,7 @@ ], "rhythm": "непрерывно, по мере прихода сообщений", "actor": "Чтец `stg.hits_raw_kafka` и матвью `stg.hits_raw_mv`.", + "join": "Соединения нет: сообщение кладется строкой целиком.", "links": [ { "label": "SQL", @@ -847,6 +848,7 @@ ], "rhythm": "непрерывно, по мере прихода сообщений", "actor": "Матвью `ods.event_mv` разбирает годные строки.", + "join": "Соединения нет: матвью разбирает строку в колонки.", "links": [ { "label": "SQL", @@ -868,6 +870,7 @@ ], "rhythm": "непрерывно, по мере прихода сообщений", "actor": "Матвью `ods.event_errors_mv` отбирает строки, которые не прошли строгий прием.", + "join": "Соединения нет: негодная строка уходит целиком вместе с классом ошибки.", "links": [ { "label": "SQL", @@ -889,6 +892,7 @@ ], "rhythm": "пачкой, раз в модельные сутки", "actor": "Задача `pull_batch` дага `orders_ingest` читает `stg.orders_raw_kafka`.", + "join": "Соединения нет: сообщение слепка кладется целиком, пачка помечена `_load_id`.", "links": [ { "label": "SQL", @@ -910,6 +914,7 @@ ], "rhythm": "пачкой, раз в модельные сутки", "actor": "Задача `parse_batch` дага `orders_ingest` пишет годные версии заказа.", + "join": "Соединения нет: строка разбирается в колонки, версия задается `snapshot_date`.", "links": [ { "label": "SQL", @@ -931,6 +936,7 @@ ], "rhythm": "пачкой, раз в модельные сутки", "actor": "Задача `parse_batch` дага `orders_ingest` пишет негодные строки той же пачки.", + "join": "Соединения нет: негодная строка пачки уходит целиком.", "links": [ { "label": "SQL", @@ -952,6 +958,7 @@ ], "rhythm": "без копии, данные читаются на месте", "actor": "Запрос `dds.event_v` читает данные из `ods.event_v` и переименовывает поля на языке бизнеса.", + "join": "Соединения нет: представление переименовывает колонки той же строки.", "links": [ { "label": "SQL", @@ -973,6 +980,7 @@ ], "rhythm": "пачкой, при каждом прогоне `dds_transform`", "actor": "Задачи `session.rebuild` и `session.replace` дага `dds_transform`.", + "join": "События одной `client_id` за `event_date` режутся по паузам; `session_id` - `cityHash64(client_id, min(event_time))`.", "links": [ { "label": "SQL сборки", @@ -998,6 +1006,7 @@ ], "rhythm": "пачкой, при каждом прогоне `dds_transform`", "actor": "Задачи `order.rebuild` и `order.replace` дага `dds_transform`.", + "join": "Соединения нет: из заказа берутся поля и позиции, `order_date` выводится из `created_at`.", "links": [ { "label": "SQL сборки", @@ -1023,6 +1032,7 @@ ], "rhythm": "пачкой, при каждом прогоне `dds_transform`", "actor": "Задача `identity_map` дага `dds_transform` соединяет покупки с заказами.", + "join": "Строки покупки и заказа соединяются по `purchase_id[1] = order_id`.", "links": [ { "label": "SQL", @@ -1044,6 +1054,7 @@ ], "rhythm": "пачкой, при каждом прогоне `dds_transform`", "actor": "Задача `identity_map` дага `dds_transform` соединяет заказы с покупками.", + "join": "Строки заказа и покупки соединяются по `order_id = purchase_id[1]`; берется `user_id`.", "links": [ { "label": "SQL", @@ -1065,6 +1076,7 @@ ], "rhythm": "пачкой, при каждом прогоне `dm_transform`", "actor": "Задача `daily_traffic` дага `dm_transform` считает посетителей по сессиям.", + "join": "Соединения нет: сессии группируются по `session_date`, который становится `report_date`.", "links": [ { "label": "SQL", @@ -1086,6 +1098,7 @@ ], "rhythm": "пачкой, при каждом прогоне `dm_transform`", "actor": "Задача `daily_traffic` дага `dm_transform` считает известных пользователей по `client_id`.", + "join": "GLOBAL LEFT JOIN по `client_id`; сессии без пары остаются в витрине.", "links": [ { "label": "SQL", @@ -1107,6 +1120,7 @@ ], "rhythm": "пачкой, при каждом прогоне `dm_transform`", "actor": "Задачи `revenue_daily.rebuild` и `revenue_daily.replace` дага `dm_transform`.", + "join": "Соединения таблиц нет: ARRAY JOIN раскрывает `item_sku`, `item_quantity`, `item_price`; `order_date` становится `report_date`.", "links": [ { "label": "SQL сборки", @@ -1132,6 +1146,7 @@ ], "rhythm": "пачкой, при каждом прогоне `dm_transform`", "actor": "Даг `dm_transform` получает категорию через `dictGet` по `item_sku`; ключ словаря — `sku`.", + "join": "Категория читается через `dictGet` по `item_sku`; ключ справочника - `sku`.", "links": [ { "label": "SQL", @@ -1153,6 +1168,7 @@ ], "rhythm": "пачкой, при каждом прогоне `dm_transform`", "actor": "Задачи `purchase_vs_orders.rebuild` и `purchase_vs_orders.replace` дага `dm_transform` читают покупки.", + "join": "Покупки группируются по `purchase_id[1]` как `purchase_order_id`; `event_date` становится `purchase_day`, затем GLOBAL FULL OUTER JOIN `purchase_order_id = backend_order_id`.", "links": [ { "label": "SQL сборки", @@ -1178,6 +1194,7 @@ ], "rhythm": "пачкой, при каждом прогоне `dm_transform`", "actor": "Задачи `purchase_vs_orders.rebuild` и `purchase_vs_orders.replace` дага `dm_transform` читают заказы.", + "join": "GLOBAL FULL OUTER JOIN: `purchase_order_id = backend_order_id` (`purchase_id[1]` и `order_id`); отсутствующая сторона получает NULL.", "links": [ { "label": "SQL сборки", @@ -1304,6 +1321,7 @@ addSection("Механизм", mechanismById.get(edge.mechanism).label); addSection("Ритм", edge.rhythm); addSection("Кто именно", edge.actor); + addSection("Как соединяет", edge.join); addLinks(edge.links); } function layoutMap() { diff --git a/docs/handbook/architecture-map/map.yaml b/docs/handbook/architecture-map/map.yaml index aa1ad18..8b5c87a 100644 --- a/docs/handbook/architecture-map/map.yaml +++ b/docs/handbook/architecture-map/map.yaml @@ -453,6 +453,7 @@ edges: streams: [events] rhythm: непрерывно, по мере прихода сообщений actor: Чтец `stg.hits_raw_kafka` и матвью `stg.hits_raw_mv`. + join: 'Соединения нет: сообщение кладется строкой целиком.' links: - label: SQL url: https://git.dementev.space/ddmitry/clickstream-data-platform/src/branch/main/sql/ddl/40-stg-views.sql @@ -466,6 +467,7 @@ edges: streams: [events] rhythm: непрерывно, по мере прихода сообщений actor: Матвью `ods.event_mv` разбирает годные строки. + join: 'Соединения нет: матвью разбирает строку в колонки.' links: - label: SQL url: https://git.dementev.space/ddmitry/clickstream-data-platform/src/branch/main/sql/ddl/30-ods-views.sql @@ -479,6 +481,7 @@ edges: streams: [events] rhythm: непрерывно, по мере прихода сообщений actor: Матвью `ods.event_errors_mv` отбирает строки, которые не прошли строгий прием. + join: 'Соединения нет: негодная строка уходит целиком вместе с классом ошибки.' links: - label: SQL url: https://git.dementev.space/ddmitry/clickstream-data-platform/src/branch/main/sql/ddl/30-ods-views.sql @@ -492,6 +495,7 @@ edges: streams: [orders] rhythm: пачкой, раз в модельные сутки actor: Задача `pull_batch` дага `orders_ingest` читает `stg.orders_raw_kafka`. + join: 'Соединения нет: сообщение слепка кладется целиком, пачка помечена `_load_id`.' links: - label: SQL url: https://git.dementev.space/ddmitry/clickstream-data-platform/src/branch/main/sql/stg/orders_raw_load.sql @@ -505,6 +509,7 @@ edges: streams: [orders] rhythm: пачкой, раз в модельные сутки actor: Задача `parse_batch` дага `orders_ingest` пишет годные версии заказа. + join: 'Соединения нет: строка разбирается в колонки, версия задается `snapshot_date`.' links: - label: SQL url: https://git.dementev.space/ddmitry/clickstream-data-platform/src/branch/main/sql/ods/order_parse.sql @@ -518,6 +523,7 @@ edges: streams: [orders] rhythm: пачкой, раз в модельные сутки actor: Задача `parse_batch` дага `orders_ingest` пишет негодные строки той же пачки. + join: 'Соединения нет: негодная строка пачки уходит целиком.' links: - label: SQL url: https://git.dementev.space/ddmitry/clickstream-data-platform/src/branch/main/sql/ods/order_parse.sql @@ -531,6 +537,7 @@ edges: streams: [events] rhythm: без копии, данные читаются на месте actor: Запрос `dds.event_v` читает данные из `ods.event_v` и переименовывает поля на языке бизнеса. + join: 'Соединения нет: представление переименовывает колонки той же строки.' links: - label: SQL url: https://git.dementev.space/ddmitry/clickstream-data-platform/src/branch/main/sql/ddl/50-dds-views.sql @@ -544,6 +551,7 @@ edges: streams: [events] rhythm: пачкой, при каждом прогоне `dds_transform` actor: Задачи `session.rebuild` и `session.replace` дага `dds_transform`. + join: 'События одной `client_id` за `event_date` режутся по паузам; `session_id` - `cityHash64(client_id, min(event_time))`.' links: - label: SQL сборки url: https://git.dementev.space/ddmitry/clickstream-data-platform/src/branch/main/sql/dds/session_rebuild.sql @@ -559,6 +567,7 @@ edges: streams: [orders] rhythm: пачкой, при каждом прогоне `dds_transform` actor: Задачи `order.rebuild` и `order.replace` дага `dds_transform`. + join: 'Соединения нет: из заказа берутся поля и позиции, `order_date` выводится из `created_at`.' links: - label: SQL сборки url: https://git.dementev.space/ddmitry/clickstream-data-platform/src/branch/main/sql/dds/order_rebuild.sql @@ -574,6 +583,7 @@ edges: streams: [events] rhythm: пачкой, при каждом прогоне `dds_transform` actor: Задача `identity_map` дага `dds_transform` соединяет покупки с заказами. + join: 'Строки покупки и заказа соединяются по `purchase_id[1] = order_id`.' links: - label: SQL url: https://git.dementev.space/ddmitry/clickstream-data-platform/src/branch/main/sql/dds/identity_map_rebuild.sql @@ -587,6 +597,7 @@ edges: streams: [orders] rhythm: пачкой, при каждом прогоне `dds_transform` actor: Задача `identity_map` дага `dds_transform` соединяет заказы с покупками. + join: 'Строки заказа и покупки соединяются по `order_id = purchase_id[1]`; берется `user_id`.' links: - label: SQL url: https://git.dementev.space/ddmitry/clickstream-data-platform/src/branch/main/sql/dds/identity_map_rebuild.sql @@ -600,6 +611,7 @@ edges: streams: [events] rhythm: пачкой, при каждом прогоне `dm_transform` actor: Задача `daily_traffic` дага `dm_transform` считает посетителей по сессиям. + join: 'Соединения нет: сессии группируются по `session_date`, который становится `report_date`.' links: - label: SQL url: https://git.dementev.space/ddmitry/clickstream-data-platform/src/branch/main/sql/dm/daily_traffic_rebuild.sql @@ -613,6 +625,7 @@ edges: streams: [events] rhythm: пачкой, при каждом прогоне `dm_transform` actor: Задача `daily_traffic` дага `dm_transform` считает известных пользователей по `client_id`. + join: 'GLOBAL LEFT JOIN по `client_id`; сессии без пары остаются в витрине.' links: - label: SQL url: https://git.dementev.space/ddmitry/clickstream-data-platform/src/branch/main/sql/dm/daily_traffic_rebuild.sql @@ -626,6 +639,7 @@ edges: streams: [orders] rhythm: пачкой, при каждом прогоне `dm_transform` actor: Задачи `revenue_daily.rebuild` и `revenue_daily.replace` дага `dm_transform`. + join: 'Соединения таблиц нет: ARRAY JOIN раскрывает `item_sku`, `item_quantity`, `item_price`; `order_date` становится `report_date`.' links: - label: SQL сборки url: https://git.dementev.space/ddmitry/clickstream-data-platform/src/branch/main/sql/dm/revenue_daily_rebuild.sql @@ -641,6 +655,7 @@ edges: streams: [orders] rhythm: пачкой, при каждом прогоне `dm_transform` actor: Даг `dm_transform` получает категорию через `dictGet` по `item_sku`; ключ словаря — `sku`. + join: 'Категория читается через `dictGet` по `item_sku`; ключ справочника - `sku`.' links: - label: SQL url: https://git.dementev.space/ddmitry/clickstream-data-platform/src/branch/main/sql/dm/revenue_daily_rebuild.sql @@ -654,6 +669,7 @@ edges: streams: [events] rhythm: пачкой, при каждом прогоне `dm_transform` actor: Задачи `purchase_vs_orders.rebuild` и `purchase_vs_orders.replace` дага `dm_transform` читают покупки. + join: 'Покупки группируются по `purchase_id[1]` как `purchase_order_id`; `event_date` становится `purchase_day`, затем GLOBAL FULL OUTER JOIN `purchase_order_id = backend_order_id`.' links: - label: SQL сборки url: https://git.dementev.space/ddmitry/clickstream-data-platform/src/branch/main/sql/dm/purchase_vs_orders_rebuild.sql @@ -669,6 +685,7 @@ edges: streams: [orders] rhythm: пачкой, при каждом прогоне `dm_transform` actor: Задачи `purchase_vs_orders.rebuild` и `purchase_vs_orders.replace` дага `dm_transform` читают заказы. + join: 'GLOBAL FULL OUTER JOIN: `purchase_order_id = backend_order_id` (`purchase_id[1]` и `order_id`); отсутствующая сторона получает NULL.' links: - label: SQL сборки url: https://git.dementev.space/ddmitry/clickstream-data-platform/src/branch/main/sql/dm/purchase_vs_orders_rebuild.sql diff --git a/docs/handbook/architecture-map/template.html b/docs/handbook/architecture-map/template.html index 0a018ce..116a4e7 100644 --- a/docs/handbook/architecture-map/template.html +++ b/docs/handbook/architecture-map/template.html @@ -256,6 +256,7 @@ addSection("Механизм", mechanismById.get(edge.mechanism).label); addSection("Ритм", edge.rhythm); addSection("Кто именно", edge.actor); + addSection("Как соединяет", edge.join); addLinks(edge.links); } function layoutMap() {