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

Catalog Developers API — реестр методов и общие правила области

Реестр developers-методов ЛК застройщика (контекст catalog, сервис Pin.Catalog.Api, area developers) и общие правила этой области: модель актора (менеджер застройщика), permission-модель Scoped-RBAC, tenant isolation, общий формат ответа и общий поток обработки запроса. Документы методов ссылаются сюда и описывают только свою дельту. Продвижение (покупка пакетов) вынесено в отдельный контекст promotion — его developers-методы не здесь (ADR-0060). Дома полей — ../../domain/.

Metadata

ПолеЗначение
Сервис/контекстcatalog
API areadevelopers
Тип документаapi (area-registry, не единичная операция)
Статусdraft
OwnerPIN Marketplace
Область действияранжирование застройщика (DeveloperRankingRule) в scope компании-застройщика; витрина/поиск — ../README.md; оверрайды — ../admin/; продвижение — контекст promotion
AssumptionsScoped-RBAC и claim CompanyId выдаёт identity в JWT; дом сущности — ranking-rule.md

Реестр методов

Base path области — /api/developers/v1. Поля целей и инварианты — в доме сущности.

Ранжирование (feature group ranking)

МетодRoutePermissionДокумент
GET/ranking-rulescatalog.ranking.viewlist-ranking-rules.md
POST/ranking-rulescatalog.ranking.managecreate-ranking-rule.md
PUT/ranking-rules/{ruleId}/prioritycatalog.ranking.manageupdate-ranking-rule-priority.md
POST/ranking-rules/{ruleId}/enable (+ …/disable-дельта)catalog.ranking.manageenable-ranking-rule.md
DELETE/ranking-rules/{ruleId}catalog.ranking.managedelete-ranking-rule.md

Продвижение (покупка/отмена/список пакетов и покупок) — developers-область контекста promotion (ADR-0060), permissions promotion.purchases.* / promotion.packages.view; здесь его нет.

Модель актора

ПроверкаЗначение
Actor typeUser — менеджер застройщика (JWT identity; UserId, CompanyId, scope-претензии в claims)
АутентификацияBearer JWT (подпись, exp, notBefore); нет/просрочен → 401. Гость/реф-cookie в области недопустимы (в отличие от public-ленты)
Что делаетчитает и правит только свои правила ранжирования в рамках компании
Записи о гостене создаёт — область только для авторизованных менеджеров

Актор и permission определяются до выполнения бизнес-логики; CompanyId становится TenantId tenanted-запросов (см. ниже).

Permission-модель области (Scoped-RBAC)

Дом прав — identity/domain/permission.md; permission проверяется в scope (организация → группа проектов → проект), выданном актору.

PermissionДействиеScopeМетоды
catalog.ranking.viewчтение правил ранжированияорганизация и нижеlist-ranking-rules
catalog.ranking.manageсоздание/изменение/enable-disable/удаление правилорганизация и ниже; при scope «проект/группа» цель обязана входить в scope-проекты актораcreate/update-priority/enable/delete-ranking-rule

Отсутствие требуемого permission в scope (или цель вне scope-проектов актора) → 403 FORBIDDEN до выполнения бизнес-логики.

Tenant isolation

ADR-0052: DeveloperRankingRule — tenanted-сущность с TenantId = CompanyId застройщика.

  • Мутирующие и списочные запросы своих данных изолированы по tenant; TenantId берётся из identity (claims), не из тела запроса.
  • К каждой выборке и мутации tenanted-сущности автоматически добавляется условие TenantId = CompanyId: застройщик физически не видит и не правит чужие правила — чужой ruleId неотличим от несуществующего (404).
  • Дополнительный record-level контроль не нужен: изоляция целиком на tenant + Scoped-RBAC.

Идемпотентность и конкурентность

Область не использует dedicated Idempotency-Key — идемпотентность естественная, своя у каждого метода:

  • create — unique partial index на цель (INV-1): повтор упирается в CATALOG_RANKING_RULE_DUPLICATE.
  • update-priorityPUT абсолютного значения Priority: повтор устанавливает то же значение (эффект тот же).
  • enable/disable — переход по статусу идемпотентен: повтор в целевом статусе — no-op успех (CardsRecomputed = 0).
  • delete — soft-delete: повтор упирается в уже снятое правило (CATALOG_RANKING_RULE_NOT_FOUND).

Конкурентность. Мутации правил и пересчёт DeveloperPriority карточек защищены оптимистической блокировкой. Конкуренты: два менеджера одной компании либо менеджер против консюмера источников (chessboard/invest-analytics/rewards), правящего те же карточки. Проигравший по версии получает CONCURRENCY_CONFLICT (409), перечитывает состояние и повторяет; карточки никогда не остаются в несогласованном виде — пересчёт каскада INV-5 детерминирован и фиксируется атомарно с мутацией правила. Гонка двух create на одну цель разрешается unique-индексом (CATALOG_RANKING_RULE_DUPLICATE проигравшему).

Общий формат ответа и списки

  • Ошибки — ProblemDetails с машинным code (таблица ниже); correlation — сквозной trace через traceparent.
  • Списочные методы возвращают пагинированный контейнер (Items, TotalCount, Page, PageSize) — сырой массив наружу не отдаётся.
  • Модели строго типизированы и именованы (item-модель на список, result-модель на команду); enum — строкой; голых object/json нет.

Алгоритм (общий поток запроса области)

Единый поток, общий для всех методов области; документы методов описывают только дельту:

  1. Контекст и доступ. Актор — менеджер застройщика (Bearer JWT → UserId, CompanyId, scope); команды области идемпотентны естественно (per-method: PUT абсолютного значения, unique-индексы, no-op-переходы); dedicated idempotency-header не применяется.
  2. Проверка входа и доступа. Permission-policy (catalog.ranking.*) → FORBIDDEN; форматы, enum-строки, диапазоны → ValidationError. Существование/состояние/владение целей (INV-* домена) — per-method.
  3. Что читаем. Агрегат по id (правило), батч целей, списки — по фильтрам, сортировке, пагинации; к выборкам tenanted-сущностей автоматически добавляется TenantId = CompanyId.
  4. Правила и переходы + маппинг. Инварианты и переходы From→To правила DeveloperRankingRule, доменные события; маппинг поле-в-поле в result-модель — per-method. Чтение — переходов нет.
  5. Что меняется (только записи). Создание/обновление/удаление правила; денормализация DeveloperPriority затронутых карточек — в той же транзакции; правка не должна затирать чужую (конкурентный доступ).
  6. События. Только доменные (пересчёт карточек в том же коммите; Kafka каталог по developers-области не публикует).
  7. Согласованность (только записи). Правило + денормализация карточек — в одном согласованном изменении.
  8. Результат. Модель/статус per-method (200/201); бизнес-ошибка → ProblemDetails с кодом.

Диаграмма

Общие ошибки области

Кросс-методные коды; доменные коды правил — в доме сущности и документах методов.

КодHTTP statusУсловиеRetry
ValidationError400Невалидные форматы/enum/диапазоны запросаno retry
UNAUTHORIZED401Нет/просрочен Bearer JWTno retry
FORBIDDEN403Нет требуемого permission в scope либо цель вне scope-проектов актораno retry
CONCURRENCY_CONFLICT409Конфликт версий (гонка с консюмером источников или параллельной правкой)retry

Доменные коды: ранжирование — CATALOG_RANKING_* (ranking-rule.md, INV-1..5).

Наблюдаемость и совместимость (общие правила area)

  • Logs: operation, tenantId (CompanyId), actor userId, результат; PII в области нет (приоритеты — не секрет). Metrics: catalog_developers_operations_total{operation,result}, latency histogram per route, catalog_projection_recompute_total{trigger} (ranking). Traces: span на handler, correlation — сквозной trace через traceparent. Dashboard catalog-overview; runbook — сверка/rebuild проекций (../../README.md, Runtime).
  • Совместимость: новые optional-поля моделей — additive; переименование/удаление/смена типа поля, сужение прав — breaking (migration + rollout). Неизвестные значения enum в запросе → ValidationError.

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