Перейти к основному содержимому

Внешнее архитектурное ревью платформы (30.04.2026) и постатейные ответы

Версия: 1.0 Дата: 30.04.2026 Статус: Готов к обсуждению

Назначение документа

Документ фиксирует внешнее архитектурное ревью, полученное по абстрактному техническому заданию платформы 30.04.2026, и постатейные ответы главного архитектора по каждому пункту: согласие или несогласие с тезисом, предлагаемые изменения в документации платформы, приоритетность правок.

Документ создан как audit trail архитектурного процесса — каждое серьёзное внешнее замечание должно оставлять след: получено, проанализировано, отвечено, последующие документы обновлены или мотивированно не обновлены.

Источник ревью

Ревью получено от внешнего эксперта на основе абстрактного технического задания. Эксперт оценивал архитектурный замысел, оси платформы, логические связи, gap'ы и зрелость документации.

Полный текст ревью

1. Общий вердикт: «Промышленный радикализм»

Концепция Vitiana — это попытка построить Stripe в мире Travel. Вы отказываетесь от модели «агрегатора-посредника» в пользу «платформы-инфраструктуры».

Главный успех концепта: Выход за рамки «отельной выдачи». Платформа мыслит Офферами и Квотами, а не «комнатами». Это позволяет строить Tour Builder как ядро, а не как надстройку.

2. Анализ Осей и Логических Связей

Ось 1: Каноническая доменная модель (Domain Spine)

Сила: Четкое разделение на Master Truth (стабильное), Operational Truth (быстрое/волатильное) и Transactional Truth (неизменное). Это защищает систему от «отравления» кэшами.

Логический разрыв: В Booking State Machine (14 состояний) состояние unknown_external_state (8-е) заявлено как «first-class категория». Это смело, но опасно. Если этот статус не имеет жесткого автоматического тайм-аута с переходом в Failed или Manual_Intervention, система накопит «зависшие» деньги. В финансовом блоке (partner-finance-and-clearing) не до конца прописано, блокируется ли баланс партнера «навсегда» при этом статусе.

Пропуск: Не детализирована модель Inventory для не-отельных сегментов (перелеты, страховки). Отели прописаны глубоко, остальное — декларативно. Для Tour Builder это критично: логика Quote для авиабилета (где цена живет секунды) принципиально иная, чем для отеля.

Ось 2: Поверхности (Surfaces)

Сила: Принцип «API-as-a-Product» и отказ от «одного API для всех». Visibility Matrix — отличный инструмент.

Логический конфликт: Вы заявляете Partner Machine Surface и Partner UI/SDK как равноправные. Однако в SDK часто зашивают логику, которая в «чистом» API может отсутствовать. Есть риск, что партнеры на чистом API будут «гражданами второго сорта» по сравнению с теми, кто использует ваш SDK.

Связанность: Связь Surface → Capability Set → ActorContext прописана идеально. Это база для безопасности.

Ось 3: Платформа как продукт (Monetization)

Сила: Динамическая тарификация за труд платформы (сложность поиска, нагрузка на поставщиков). Это отвязывает ваш доход от маржи отеля и привязывает к вашим затратам на инфраструктуру. Гениально с точки зрения Unit-экономики.

Риск: Поймет ли рынок плату за «сложность запроса»? Большинство B2B привыкло к комиссии. Вам понадобится очень прозрачный Usage Dashboard, чтобы партнер не чувствовал, что его «грабят» за тяжелые фильтры.

Ось 4: Данные и Интеллект

Сила: Событийная модель с 6 классами гарантий. Разделение Domain и Analytical событий на уровне шины — это стандарт High-load, который вы закладываете с нулевого дня.

Критика: ML-платформа описана как «first-class», но в Stage 1-2 она фактически отсутствует. Есть риск, что вы накопите данные в DWH в формате, который потом потребует мучительного ре-инжиниринга для обучения моделей ранжирования.

3. Критические Gaps (Пропуски)

Ingestion Bottleneck (Бутылочное горлышко приема): Правило 00000 (Платформа доминирует) возлагает на Ingestion Layer колоссальную нагрузку. Нормализация данных от 50 поставщиков в единую модель Property — это не только техническая, но и лингвистическая/географическая проблема. В документах не описан механизм Conflict Resolution, когда два доверенных поставщика дают противоречивые данные (например, разные координаты одного отеля). Кто «правее» в автоматическом режиме?

Дрейф цен в Tour Builder (The Drift Problem): В tour-builder-operational-model описан DriftEvent. Но как быть с «каскадным дрейфом»? Если в туре из 5 элементов изменилась цена одного, это может сделать невыгодным весь тур. Не хватает логики автоматического переподбора (Auto-replace) при дрифте, которая была бы бесшовной для агента.

Settlement при частичных отказах: Saga в Tour Builder — отличное решение. Но финансовая модель Refund при отмене одного элемента из пакета (Package Travel Directive) прописана сложно. Нужно четко разделить: что возвращает поставщик и что обязана вернуть платформа (insolvency protection).

5. Резюме по архитектурным правилам

  • Правило 00000: Соблюдено на 100%. Это фундамент, который выстоит.
  • Развитие без деградации: Соблюдено. Отсутствие «заглушек» в тексте радует, вы честно фиксируете «развилки».
  • Тезисное обоснование: Имеется почти везде. Это делает ТЗ понятным для Senior-подрядчика.

Финальный вывод

Архитектура готова к реализации Stage 1. Она избыточна для простого сайта, но адекватна для глобальной платформы.

Главный совет: Сфокусируйтесь на Governance данных (матчинг и дедупликация). Если на старте каноническая модель наполнится дублями и мусором от поставщиков, все остальные оси (ML, Search, Tour Builder) потеряют смысл, так как будут работать на гнилом фундаменте.

Постатейные ответы главного архитектора

Ответ 1: «Промышленный радикализм» — принимаю формулировку

Согласен полностью. «Stripe в travel» — короткая ёмкая формулировка, объясняющая партнёрам и подрядчикам в одной фразе суть архитектуры.

Действие: добавить эту формулировку в overview/architectural-anchor-and-business-model.md как краткий elevator pitch в начале документа.

Ответ 2.1: unknown_external_state без жёсткого timeout — критическая правка

Согласен полностью. Это реальный пробел архитектуры. У нас есть reference/booking-state-machine.md с 14 состояниями, и unknown_external_state действительно first-class. Но timeout-эскалация в нём задокументирована недостаточно жёстко.

Действие: усилить документацию явными правилами:

  • Жёсткий timeout перед автоматическим переходом из unknown_external_state:
    • search-флоу — 15 минут;
    • booking-флоу — 4 часа;
    • после timeout — переход в failed (если есть подтверждение неудачи) или manual_intervention (если поставщик молчит).
  • Состояние никогда не остаётся в unknown_external_state навсегда.
  • В reference/partner-finance-and-clearing.md явное правило: средства на партнёрском балансе не блокируются дольше N часов; по истечении timeout — либо list_failed_charge, либо escalation.
  • Метрика unknown_external_state_share уже зафиксирована как один из 10 SLI в operations/sla-and-on-call-model.md — связать с runbook'ом инцидента «доля > 1%».

Приоритет: Block 1 (критично к Stage 1).

Ответ 2.2: Inventory не-отельных сегментов — закладываем сразу

Согласен. Известный пробел — в reference/tour-builder-domain.md и reference/domain-model.md отели проработаны вглубь, а flights / transfers / insurance / activities — на уровне «то же самое, только с поправкой».

Ревьюер прав в важном: семантика Quote для авиабилета принципиально иная — цена живёт секунды, не минуты; pricing engine внешний (GDS/IATA); refund-семантика жёстко регулируется (IATA fare rules); quote-revalidation должна быть встроена в чек-аут.

Действие: создать новый документ reference/inventory-non-accommodation-domains.md, описывающий 4 не-отельных segment-типа с явным тезисом «у каждого segment-типа своя Quote-семантика», и перекрёстные ссылки из tour-builder-domain.md.

Не блокирует Stage 1 (отели сначала), но закладывает каноничную модель сразу, чтобы потом не переделывать ядро.

Приоритет: Block 2 (закладка для Stage 2-3).

Ответ 2.3: SDK как тонкая обёртка — наше решение жёстче ревьюера

Замечание правильное, но решение иное. Ревьюер боится что партнёры на чистом API будут «гражданами второго сорта», если в SDK зашивается логика которой нет в API.

Наше каноничное решение наоборот: SDK не должен ничего добавлять сверх API. SDK — это тонкая обёртка с типизированными моделями, retry, idempotency. Любая бизнес-логика — на стороне API. Если в SDK появилось правило, которого нет в API — это баг SDK, не фича.

Так делает Stripe: Stripe SDK ничего не знает, чего не знает Stripe API.

Действие: добавить в reference/api-as-product.md явный тезис «SDK как тонкая обёртка»:

  • SDK не содержит бизнес-логики.
  • Любая валидация, идемпотентность, политика — на API-стороне.
  • Каждый SDK-метод имеет точное соответствие одному API-запросу.
  • Партнёры на raw API получают тот же функционал с той же надёжностью, просто без удобств типизации.

Приоритет: Block 1 (критично к Stage 1 — это формирует культуру разработки SDK).

Ответ 2.4: Динамическая тарификация — согласен с риском, корректирую фокус

Ревьюер боится что партнёры не поймут «плату за сложность запроса». Корректирую: мы не платим за сложность — мы платим за потребление (метрики: количество search, количество quote, количество booking, количество supplier-вызовов). Это не «оценщик субъективной сложности», это «счётчик ресурсов».

Действие: зафиксировать в reference/economic-model.md:

  • Пунктовое explanation тарификации без эзотерических слов «сложность».
  • 3-4 примера типичных профилей потребления (стартующее агентство, активный B2B, enterprise).
  • Каждый платный объект (search, quote, booking, supplier-call) — с явной ценой и ставкой.

Usage Dashboard ревьюер правильно подсветил — он должен показывать партнёру в реальном времени что и за что начисляется. У нас это есть в reference/api-metering-and-usage-governance.md, но возможно стоит вытащить mockup-дашборд в отдельный раздел.

Приоритет: Block 4 (коммуникационное).

Ответ 2.5: ML-платформа в Stage 1-2 — частично согласен

Действительно, в Stage 1 ML-платформа физически отсутствует — есть только обязательство к event format, чтобы потом не переписывать collectors.

Что у нас уже зафиксировано: аналитические события с самого начала имеют схемы, готовые для ML (hashed user_id, generalized geo, exposure tracking, conversion events); DWH с partition'ами по дате и user_id; feature naming convention.

Что стоит сделать сильнее:

  • Добавить в reference/ml-platform.md раздел «Stage 1 minimum» — какие именно поля в каких events обязательны с нулевого дня для будущих ML-моделей (даже если сами модели появятся в Stage 3).
  • Список 3-5 «MVP-моделей» которые мы планируем (ranking, dynamic pricing, fraud detection, recommendation, demand forecasting) — для каждой указать какие поля events критичны на Stage 1.
  • Принцип: «накапливай данные в правильной схеме, даже если ещё не используешь».

Приоритет: Block 2 (закладка для Stage 2-3).

Ответ 3.1: Conflict Resolution в Ingestion — самый важный пункт ревью

Согласен полностью, критическая правка. В reference/data-governance-and-matching.md есть матчинг (когда две записи об одном объекте — это один объект), но не conflict resolution для противоречивых данных (когда один объект, но supplier'ы дают разные значения).

Конкретные сценарии без решения:

  • Два supplier'а вернули разные координаты одного отеля.
  • Два supplier'а вернули разные star_rating.
  • Supplier поменял координаты — это поправка ошибки или новая локация?
  • Три источника, два говорят X, один Y — большинство выигрывает или есть приоритет?

Действие: расширить data-governance-and-matching.md разделом «Conflict Resolution Policy»:

  1. Source priority rules — у каких полей какой supplier канонический (координаты — Google Places, цены — supplier который продаёт, описание — наш редактор).
  2. Voting mechanism — для полей без явного приоритета, большинство из последних N источников.
  3. Confidence scoring — каждое поле имеет confidence в зависимости от источника и согласованности.
  4. Manual review queue — при low confidence или явном расхождении — модератору.
  5. Audit trail — каждое изменение поля канонической модели хранит provenance: какой источник, когда, какой confidence.

Это must-have перед Stage 1 production. Без этого получим именно «гнилой фундамент» о котором говорит ревьюер в финальном выводе.

Приоритет: Block 1, наивысший в блоке.

Ответ 3.2: Каскадный дрейф в Tour Builder — согласен

В reference/tour-builder-operational-model.md есть DriftEvent, но auto-replace только декларативно упомянут.

Действие: дописать:

  • Каскадный анализ влияния: при дрифте одного элемента — пересчёт всего тура (новая полная цена, новые комиссии, новые комбинации).
  • Replacement candidates: для каждого элемента тура хранить ranked-список альтернатив (cheaper hotel, alternative airline, similar tour) на случай дрифта.
  • Threshold rules: total drift < 5% — auto-accept; 5-15% — auto-replace с уведомлением партнёра; > 15% — manual review партнёром.
  • Bundle integrity: проверка что замена не нарушает связи (например, замена отеля не должна нарушить расстояние до точки трансфера).

Сложная фича, но проектировать сразу нужно. Иначе при first production-tour drift'е партнёр получит «вы должны переделать всё руками» — и доверие потеряно.

Приоритет: Block 2.

Ответ 3.3: Settlement при частичных отказах — согласен

В reference/partner-finance-and-clearing.md финансовая модель refund'ов прописана, но разделение ответственности между supplier-refund и платформенным refund размыто.

Действие: явно зафиксировать:

  • Supplier portion: сколько вернёт поставщик (по контракту с нами).
  • Platform portion: сколько мы возвращаем партнёру даже если supplier не вернул (insolvency protection per Package Travel Directive).
  • Customer portion: что возвращается end-customer'у через нашего партнёра.
  • Loss accounting: разница между supplier-portion и platform-obligation — это наш убыток (ML может прогнозировать риск, partner-tier может покрывать через premium fee, или амортизация через резерв).

Связано с insurance product который сейчас декларативный — может быть стоит сразу проектировать opt-in расширенную страховку для партнёра.

Приоритет: Block 3.

Дополнение от архитектора — gap, не замеченный ревьюером

Long-running idempotency для Tour Builder

У нас есть idempotency keys для синхронных вызовов. Но Tour Builder может минутами собирать тур (саги через несколько supplier'ов). Если партнёр повторяет запрос с тем же idempotency-key через 30 секунд:

  • Возвращаем cached partial result?
  • Возвращаем error «in_progress»?
  • Игнорируем повтор?

В нашей документации это не прописано чётко.

Действие: добавить раздел «Long-running idempotency semantics» в reference/eventing-and-queue-baseline.md.

Приоритет: Block 3.

План интеграции замечаний

Block 1 — критичное к Stage 1 (срочно)

  1. Conflict Resolution Policy в data-governance-and-matching.md — самый важный gap.
  2. Timeout-эскалация для unknown_external_state в booking-state-machine.md + связь с finance.
  3. «SDK как тонкая обёртка» в api-as-product.md — снимает риск раздвоения surfaces.

Block 2 — закладка для Stage 2-3 (зафиксировать сейчас, реализовать позже)

  1. Inventory non-accommodation новый документ — закладывает семантику flights/transfers/insurance.
  2. Stage 1 minimum для ML в ml-platform.md — какие поля events обязательны с нулевого дня.
  3. Tour Builder cascading drift + auto-replace в tour-builder-operational-model.md.

Block 3 — финансовые уточнения

  1. Settlement split (supplier/platform/customer portions) в partner-finance-and-clearing.md.
  2. Long-running idempotency в eventing-and-queue-baseline.md.

Block 4 — коммуникационные

  1. «Stripe в travel» формулировка в architectural-anchor-and-business-model.md.
  2. Прозрачность тарификации в economic-model.md с примерами и mockup'ом дашборда.

Резюме

Внешнее ревью признаёт архитектуру готовой к Stage 1 при условии закрытия 3 критических gap'ов в Block 1. Все замечания по существу приняты, по двум вопросам (SDK-граждане первого сорта, тарификация за «сложность») архитекторская позиция уточнена, не отвергнута.

Главный совет ревьюера — «сфокусируйтесь на Governance данных» — соответствует Block 1 нашего плана интеграции (пункт 1: Conflict Resolution Policy).

Связанная документация

Документы, которые будут обновлены или созданы по итогам ревью: