docs(internal): разделён PRD на стратегию и дизайн задания

- Зачем:
  - PRD стал перегруженным — смешаны стратегические и тактические решения
- Что:
  - PRD.md сокращён до стратегии (видение, аудитория, скоуп, критерии, риски)
  - создан assignment_design.md (эталонный срез, порядок выполнения, SCD2-подход, валидационный DAG, формат ТЗ)
  - зафиксированы решения: эталон sales_report, валидационный DAG, SCD2 как задание с подсказками
- Проверка:
  - просмотр файлов docs/internal/PRD.md и docs/internal/assignment_design.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 18:35:24 +03:00
co-authored by Claude Opus 4.6
parent d77da5686c
commit df8c2c36fa
2 changed files with 160 additions and 153 deletions
+21 -153
View File
@@ -134,30 +134,24 @@ pgmeta (Postgres 16) ─────────────> Airflow (webserver
## 6. Педагогическая модель ## 6. Педагогическая модель
Подробности — в [assignment_design.md](assignment_design.md).
### Принцип: «Эталонный срез + ТЗ» ### Принцип: «Эталонный срез + ТЗ»
Студент получает репозиторий, в котором: Студент получает репозиторий, в котором:
1. **Эталонный вертикальный срез** — полностью реализованная цепочка для одной 1. **Эталонный вертикальный срез** — полностью реализованная цепочка
витрины DM и всех её источников вниз по слоям (STG → ODS → DDS → DM). `sales_report` и все её источники вниз по слоям (STG → ODS → DDS → DM).
Это — образец, на который студент ориентируется. 2. **ТЗ от аналитика** — описание остальных таблиц
([analyst_spec.md](../assignment/analyst_spec.md)).
2. **ТЗ от аналитика** — Markdown-документ с описанием остальных таблиц: 3. **Частично готовый DAG** — студент добавляет свои таски по аналогии.
маппинги полей, бизнес-правила, ожидаемая гранулярность, тип SCD. 4. **Валидационный DAG** — студент запускает для самоконтроля.
3. **Частично готовый DAG** — Python-файлы DAG с реализованными тасками
эталонного среза. Студент добавляет свои таски по аналогии.
4. **DQ-проверки**<!-- TODO: проработать формат --> готовые проверки,
которые студент запускает для самоконтроля.
### Что делает студент ### Что делает студент
- Пишет DDL для назначенных таблиц (`*_ddl.sql`) - Пишет DDL, SQL-загрузки, DQ-проверки для назначенных таблиц
- Пишет SQL-загрузки (`*_load.sql`)
- При необходимости пишет DQ-проверки (`*_dq.sql`)
- Добавляет таски в существующий DAG - Добавляет таски в существующий DAG
- Проверяет результат через DQ и запросы в Greenplum - Проверяет результат через валидационный DAG и запросы в Greenplum
### Что студент НЕ делает ### Что студент НЕ делает
@@ -166,131 +160,9 @@ pgmeta (Postgres 16) ─────────────> Airflow (webserver
- Не настраивает Airflow Connections (преднастроены) - Не настраивает Airflow Connections (преднастроены)
- Не работает с PXF-конфигурацией (настроен) - Не работает с PXF-конфигурацией (настроен)
### Выбор эталонного среза
### Эталонный срез: витрина `sales_report`
Эталоном выбрана витрина `dm.sales_report` и вся её цепочка вниз по слоям.
**Почему `sales_report`:**
- Покрывает SCD1 (airports, tariffs), HWM-инкремент, fact load
- Богатая денормализация — хороший образец для подражания
- Средняя сложность — не пугает, но и не тривиальна
**Эталонные таблицы (даны студенту):**
| Слой | Таблицы |
|------|-----------------------------------------------------------------|
| DM | `sales_report` |
| DDS | `fact_flight_sales`, `dim_airports` (SCD1), `dim_tariffs` (SCD1), `dim_calendar` |
| ODS | `bookings`, `tickets`, `segments`, `flights`, `boarding_passes`, `airports` |
| STG | `bookings`, `tickets`, `segments`, `flights`, `boarding_passes`, `airports` |
**Задание студенту:**
| Слой | Таблицы | Что нового для студента |
|------|-------------------------------------------------------------------|--------------------------------------------------|
| STG | `airplanes`, `seats`, `routes` | Практика по аналогии с эталоном |
| ODS | `airplanes`, `seats`, `routes` | Практика SCD1 UPSERT по аналогии |
| DDS | `dim_airplanes` (SCD1), `dim_passengers` (SCD1), `dim_routes` (SCD2) | **SCD2 — ключевой вызов курсовой** |
| DM | `airport_traffic`, `monthly_overview`, `route_performance`, `passenger_loyalty` | Разная сложность (от простой к сложной) |
**Рекомендуемый порядок выполнения для студента:**
1. STG (airplanes, seats, routes) — разминка, по аналогии
2. ODS (airplanes, seats, routes) — закрепление UPSERT
3. DDS dim_airplanes, dim_passengers (SCD1) — новые измерения
4. DDS dim_routes (**SCD2**) — ключевой вызов
5. DM airport_traffic — простая витрина, похожа на sales_report
6. DM route_performance — TRUNCATE+INSERT, SCD2-агрегация по BK
7. DM monthly_overview — двухуровневая агрегация
8. DM passenger_loyalty — самая сложная, пересчёт истории
### SCD2 в задании: подход «рецепт без готового SQL»
Реализация `dim_routes` (SCD2) — ключевой вызов курсовой. Студент делает это
самостоятельно, но ТЗ содержит пошаговую подсказку:
1. Алгоритм SCD2 текстом (без SQL):
- Вычисли `hashdiff` по набору атрибутов (атрибуты перечислены в ТЗ)
- Найди строки, у которых `hashdiff` изменился
- Закрой старую версию (`valid_to = текущая_дата`)
- Вставь новую версию (`valid_from = текущая_дата`, `valid_to = NULL`)
2. Формула hashdiff: `md5(concat_ws('|', field1, field2, ...))`
3. Ссылка на `naming_conventions.md` (поля `valid_from`, `valid_to`, `hashdiff`)
4. Напоминание: полуоткрытый интервал `[valid_from, valid_to)`
5. Если застрял — ветка `solution`
Самостоятельная реализация — ключ к запоминанию. SCD2 — обязательный вопрос
на собеседованиях DE, и студент должен уметь объяснить его на основе
собственного опыта.
--- ---
### 6.1. Валидационный DAG (`bookings_validate`) ## 7. Ветки и workflow
Отдельный DAG для самопроверки студента. Запускается вручную в Airflow UI
после реализации заданий. Таски сгруппированы по слоям — студент видит,
где именно проблема.
**Примерная структура тасков:**
```
bookings_validate
├── validate_stg
│ ├── check_stg_airplanes_exists (таблица создана, >0 строк)
│ ├── check_stg_seats_exists
│ └── check_stg_routes_exists
├── validate_ods
│ ├── check_ods_airplanes_rowcount (ODS >= STG по кол-ву уникальных BK)
│ ├── check_ods_seats_rowcount
│ ├── check_ods_routes_rowcount
│ └── check_ods_no_null_pks (PK not null)
├── validate_dds
│ ├── check_dim_airplanes_exists
│ ├── check_dim_passengers_exists
│ ├── check_dim_routes_scd2 (valid_from/valid_to корректны)
│ └── check_dim_routes_no_gaps (нет «дыр» в версиях SCD2)
└── validate_dm
├── check_airport_traffic_exists
├── check_monthly_overview_exists
├── check_route_performance_exists
└── check_passenger_loyalty_exists
```
**Реализация:** `PostgresOperator` + SQL-скрипты в `sql/validate/`.
Каждый SQL-скрипт выполняет SELECT и бросает исключение (через
`DO $$ ... RAISE EXCEPTION ... $$`), если проверка не пройдена.
Сообщения об ошибках — дружелюбные, с подсказкой что делать дальше.
**Ключевые проверки:**
- Таблицы существуют и содержат данные
- PK не содержат NULL
- SCD2: `valid_to IS NULL` для текущих версий, нет перекрытий интервалов
- Кросс-слойная консистентность (row count ODS vs STG)
- DM-витрины содержат данные за загруженные дни
---
## 7. Формат ТЗ от аналитика
Файл: `docs/assignment/analyst_spec.md` (или несколько файлов по слоям).
Для каждой таблицы-задания документ содержит:
- **Имя таблицы** и целевая схема (stg / ods / dds / dm)
- **Описание** — что хранит таблица, бизнес-смысл
- **Список полей** с типами и описанием
- **Маппинг источников** — откуда берётся каждое поле
- **Бизнес-правила и фильтры** (если есть)
- **Тип историзации** (SCD1 / SCD2 / snapshot / append)
- **Гранулярность** (одна строка = ?)
- **Distribution key** (подсказка или задание на выбор)
Формат — приближен к реальным ТЗ, которые студент встретит на работе.
---
## 8. Ветки и workflow
``` ```
main (стартовое состояние) main (стартовое состояние)
@@ -298,7 +170,7 @@ main (стартовое состояние)
├── ТЗ от аналитика ├── ТЗ от аналитика
├── Инфраструктура (Docker, Make, PXF) ├── Инфраструктура (Docker, Make, PXF)
├── Заглушки / TODO-маркеры для студенческих заданий ├── Заглушки / TODO-маркеры для студенческих заданий
└── DQ-проверки для самоконтроля └── Валидационный DAG для самоконтроля
solution (полное решение) solution (полное решение)
└── Все таблицы реализованы — эталон для самопроверки └── Все таблицы реализованы — эталон для самопроверки
@@ -314,21 +186,21 @@ solution (полное решение)
5. Запускает DDL-DAG'и (эталонные таблицы создаются) 5. Запускает DDL-DAG'и (эталонные таблицы создаются)
6. Запускает ETL-DAG'и — эталонный срез работает 6. Запускает ETL-DAG'и — эталонный срез работает
7. Реализует задания из ТЗ (SQL + таски в DAG) 7. Реализует задания из ТЗ (SQL + таски в DAG)
8. Проверяет себя через DQ 8. Проверяет себя через валидационный DAG
9. `make bookings-generate-day` — генерирует новый день, проверяет 9. `make bookings-generate-day` — генерирует новый день, проверяет
инкрементальность инкрементальность
10. Защищает работу перед ментором 10. Защищает работу перед ментором
--- ---
## 9. Критерии приёмки курсовой ## 8. Критерии приёмки курсовой
### Для студента (самопроверка) ### Для студента (самопроверка)
- [ ] Стенд поднимается (`make up`) без ошибок - [ ] Стенд поднимается (`make up`) без ошибок
- [ ] Все DAG'и проходят без failed-тасков - [ ] Все DAG'и проходят без failed-тасков
- [ ] Данные доезжают от STG до DM - [ ] Данные доезжают от STG до DM
- [ ] DQ-проверки проходят на всех реализованных таблицах - [ ] Валидационный DAG проходит на всех реализованных таблицах
- [ ] После `make bookings-generate-day` + повторного запуска DAG - [ ] После `make bookings-generate-day` + повторного запуска DAG
данные корректно доливаются (инкрементальность работает) данные корректно доливаются (инкрементальность работает)
@@ -343,7 +215,7 @@ solution (полное решение)
--- ---
## 10. Ограничения и риски ## 9. Ограничения и риски
| Риск / ограничение | Митигация | | Риск / ограничение | Митигация |
|--------------------------------------------|-------------------------------------------------| |--------------------------------------------|-------------------------------------------------|
@@ -361,7 +233,7 @@ solution (полное решение)
--- ---
## 11. Таймлайн ## 10. Таймлайн
| Когда | Что | | Когда | Что |
|------------------|------------------------------------------------------------| |------------------|------------------------------------------------------------|
@@ -370,16 +242,12 @@ solution (полное решение)
--- ---
## 12. Открытые вопросы (TODO) ## 11. Открытые вопросы (TODO)
1. ~~**Выбор эталонного среза**~~**РЕШЕНО.** Эталон: `sales_report` и её 1. ~~**Выбор эталонного среза**~~**РЕШЕНО.** См. [assignment_design.md](assignment_design.md).
цепочка. Задание: остальные 4 витрины + 3 STG/ODS + 3 DDS-измерения.
SCD2 (`dim_routes`) — задание с подсказками в ТЗ. (Раздел 6)
2. ~~**Формат DQ для самоконтроля**~~**РЕШЕНО.** Отдельный валидационный DAG 2. ~~**Формат DQ для самоконтроля**~~**РЕШЕНО.** Валидационный DAG.
(`bookings_validate.py`). Под капотом — SQL-проверки. Студент запускает DAG См. [assignment_design.md](assignment_design.md).
в Airflow UI и видит красные/зелёные таски по слоям. Дополнительный бонус —
практика чтения логов Airflow. (см. Раздел 6.1 ниже)
3. **Вынос CSV-пайплайна** — перенести `csv_to_greenplum.py`, 3. **Вынос CSV-пайплайна** — перенести `csv_to_greenplum.py`,
`csv_to_greenplum_dq.py`, `ddl_greenplum_base.py`, `helpers/greenplum.py`, `csv_to_greenplum_dq.py`, `ddl_greenplum_base.py`, `helpers/greenplum.py`,
+139
View File
@@ -0,0 +1,139 @@
# Дизайн курсового задания
> Тактические решения по нарезке задания, порядку выполнения и самопроверке.
> Стратегию и контекст см. в [PRD.md](PRD.md).
---
## 1. Эталонный срез: витрина `sales_report`
Эталоном выбрана витрина `dm.sales_report` и вся её цепочка вниз по слоям.
**Почему `sales_report`:**
- Покрывает SCD1 (airports, tariffs), HWM-инкремент, fact load
- Богатая денормализация — хороший образец для подражания
- Средняя сложность — не пугает, но и не тривиальна
### Эталонные таблицы (даны студенту)
| Слой | Таблицы |
|------|-----------------------------------------------------------------|
| DM | `sales_report` |
| DDS | `fact_flight_sales`, `dim_airports` (SCD1), `dim_tariffs` (SCD1), `dim_calendar` |
| ODS | `bookings`, `tickets`, `segments`, `flights`, `boarding_passes`, `airports` |
| STG | `bookings`, `tickets`, `segments`, `flights`, `boarding_passes`, `airports` |
### Задание студенту
| Слой | Таблицы | Что нового для студента |
|------|-------------------------------------------------------------------|--------------------------------------------------|
| STG | `airplanes`, `seats`, `routes` | Практика по аналогии с эталоном |
| ODS | `airplanes`, `seats`, `routes` | Практика SCD1 UPSERT по аналогии |
| DDS | `dim_airplanes` (SCD1), `dim_passengers` (SCD1), `dim_routes` (SCD2) | **SCD2 — ключевой вызов курсовой** |
| DM | `airport_traffic`, `monthly_overview`, `route_performance`, `passenger_loyalty` | Разная сложность (от простой к сложной) |
---
## 2. Рекомендуемый порядок выполнения
Студенту рекомендуется (но не обязательно) двигаться в таком порядке:
1. **STG** (airplanes, seats, routes) — разминка, по аналогии
2. **ODS** (airplanes, seats, routes) — закрепление UPSERT
3. **DDS** dim_airplanes, dim_passengers (SCD1) — новые измерения
4. **DDS** dim_routes (**SCD2**) — ключевой вызов
5. **DM** airport_traffic — простая витрина, похожа на sales_report
6. **DM** route_performance — TRUNCATE+INSERT, SCD2-агрегация по BK
7. **DM** monthly_overview — двухуровневая агрегация
8. **DM** passenger_loyalty — самая сложная, пересчёт истории
Порядок выстроен от простого к сложному. Каждый шаг опирается на опыт
предыдущего.
---
## 3. SCD2: подход «рецепт без готового SQL»
Реализация `dim_routes` (SCD2) — ключевой вызов курсовой. Студент делает это
самостоятельно, но ТЗ содержит пошаговую подсказку:
1. Алгоритм SCD2 текстом (без SQL):
- Вычисли `hashdiff` по набору атрибутов (атрибуты перечислены в ТЗ)
- Найди строки, у которых `hashdiff` изменился
- Закрой старую версию (`valid_to = текущая_дата`)
- Вставь новую версию (`valid_from = текущая_дата`, `valid_to = NULL`)
2. Формула hashdiff: `md5(concat_ws('|', field1, field2, ...))`
3. Ссылка на `naming_conventions.md` (поля `valid_from`, `valid_to`, `hashdiff`)
4. Напоминание: полуоткрытый интервал `[valid_from, valid_to)`
5. Если застрял — ветка `solution`
Самостоятельная реализация — ключ к запоминанию. SCD2 — обязательный вопрос
на собеседованиях DE, и студент должен уметь объяснить его на основе
собственного опыта.
---
## 4. Валидационный DAG (`bookings_validate`)
Отдельный DAG для самопроверки студента. Запускается вручную в Airflow UI
после реализации заданий. Таски сгруппированы по слоям — студент видит,
где именно проблема. Дополнительный бонус — практика чтения логов Airflow.
### Примерная структура тасков
```
bookings_validate
├── validate_stg
│ ├── check_stg_airplanes_exists (таблица создана, >0 строк)
│ ├── check_stg_seats_exists
│ └── check_stg_routes_exists
├── validate_ods
│ ├── check_ods_airplanes_rowcount (ODS >= STG по кол-ву уникальных BK)
│ ├── check_ods_seats_rowcount
│ ├── check_ods_routes_rowcount
│ └── check_ods_no_null_pks (PK not null)
├── validate_dds
│ ├── check_dim_airplanes_exists
│ ├── check_dim_passengers_exists
│ ├── check_dim_routes_scd2 (valid_from/valid_to корректны)
│ └── check_dim_routes_no_gaps (нет «дыр» в версиях SCD2)
└── validate_dm
├── check_airport_traffic_exists
├── check_monthly_overview_exists
├── check_route_performance_exists
└── check_passenger_loyalty_exists
```
### Реализация
- `PostgresOperator` + SQL-скрипты в `sql/validate/`
- Каждый SQL-скрипт выполняет SELECT и бросает исключение (через
`DO $$ ... RAISE EXCEPTION ... $$`), если проверка не пройдена
- Сообщения об ошибках — дружелюбные, с подсказкой что делать дальше
### Ключевые проверки
- Таблицы существуют и содержат данные
- PK не содержат NULL
- SCD2: `valid_to IS NULL` для текущих версий, нет перекрытий интервалов
- Кросс-слойная консистентность (row count ODS vs STG)
- DM-витрины содержат данные за загруженные дни
---
## 5. Формат ТЗ от аналитика
Файл: `docs/assignment/analyst_spec.md` (или несколько файлов по слоям).
Для каждой таблицы-задания документ содержит:
- **Имя таблицы** и целевая схема (stg / ods / dds / dm)
- **Описание** — что хранит таблица, бизнес-смысл
- **Список полей** с типами и описанием
- **Маппинг источников** — откуда берётся каждое поле
- **Бизнес-правила и фильтры** (если есть)
- **Тип историзации** (SCD1 / SCD2 / snapshot / append)
- **Гранулярность** (одна строка = ?)
- **Distribution key** (подсказка или задание на выбор)
Формат — приближен к реальным ТЗ, которые студент встретит на работе.