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

UC-OVR-008. Агент выходит из агентства, сохраняя метрики

Назначение

Cross-service сценарий развязки членства (ADR-0061, аналогия «игрок меняет клан»): агент выходит из агентства и становится независимым. Личные метрики/достижения (user-centric, агрегация по AgentUserId) сохраняются, а исторические сделки/лиды/фиксации остаются у прежней компании (снапшоты иммутабельны, back-fill запрещён). Оркестрирует identity (см. UC-IDN-010); изменения доступа и ростера потребляют crm, rewards, catalog, chessboard.

Metadata

ПолеЗначение
Статусdraft
Owning teamidentity
Primary capabilityagent-membership-detach
Risk levelhigh

Scope И Участники

УчастникРоль
Агент (сам) / Руководитель агентстваАктор: инициирует выход / исключение
identityВладелец членства/ролей: AgentMembership → Left, роль Organization → Platform
crmConsumer: обновляет ростер и user-centric метрики; исторические снапшоты не пересчитывает
rewardsConsumer: основания комиссий по AgentUserId (исторические — за прежней компанией)
catalog / chessboard / gatewayConsumer user-access-changed: пересчёт эффективных прав
notificationsУведомление руководителю об убытии из ростера

Preconditions

  • У агента активное AgentMembership (Status = Active); UserEntity.Type = Agent, Status = Active.

Основной Сценарий

  1. Агент жмёт «Выйти из агентства» (диалог: метрики сохранятся, доступ к клиентской базе агентства прекратится).
  2. Фронт → POST /api/v1/me/agency-membership/leave (см. UC-IDN-010, leave-agency).
  3. identity в одном согласованном изменении: членство переходит Active → Left (проставляется LeftAt, история найма сохраняется); у пользователя обнуляется привязка к компании (CompanyId → null, тип остаётся Agent); роль agent переезжает Organization → Platform.
  4. Публикуются интеграционные события: pin.identity.agent-membership-changed.v1 (Left), pin.identity.users.v1 (CompanyId = null), pin.identity.access.v1 (Organization → Platform).
  5. crm (consumer): обновляет ростер и user-centric метрики по AgentUserId; исторические сделки/лиды/ фиксации не трогает (иммутабельные снапшоты AgencyCompanyId/AgentUserId, ADR-0061 п.4).
  6. rewards пересчитывает основания текущих комиссий по AgentUserId; исторические комиссии остаются у прежней компании. catalog/chessboard/gateway пересчитывают эффективные права по user-access-changed.
  7. Агент видит статус «Независимый агент»; клиентская база агентства скрыта, личные метрики/достижения/лоты сохранены. Руководитель по membership-событию видит убытие из ростера.

Sequence

Постусловие

После успешной развязки гарантированно (в одном согласованном изменении в identity):

  • AgentMembership переведено Active → Left с проставленным LeftAt; запись не удаляется (история найма сохраняется);
  • у UserEntity обнулена привязка к компании (CompanyId → null), тип остаётся Agent; роль agent переехала Organization → Platform;
  • опубликованы три интеграционных события: pin.identity.agent-membership-changed.v1 (Status = Left, key = AgentUserId), pin.identity.users.v1 (CompanyId = null), pin.identity.access.v1 (Organization → Platform);
  • по обработке событий (eventual): crm обновил ростер и user-centric метрики (< 1 мин), rewards прекратил основания текущих комиссий от имени компании (< 5 мин), catalog/chessboard/gateway пересчитали эффективные права (< 5 мин), руководитель уведомлён;
  • исторические сделки/лиды/фиксации не изменены — снапшоты AgencyCompanyId/AgentUserId иммутабельны, back-fill запрещён.

Наблюдаемое пользователем: агент сразу видит статус «Независимый агент», клиентская база агентства скрыта, личные метрики/достижения/лоты сохранены; права к ресурсам агентства снимаются в пределах окна обработки user-access-changed.

Альтернативные Сценарии И Отказы

СитуацияПоведение
Руководитель исключает агентаТот же домен-метод с identity.users.manage; actor-stamping = руководитель (UC-IDN-010 A1)
Агент хочет полностью вернуться к инвесторуbecome-investor вместо leave (UC-IDN-010 A2); дополнительно снимается роль agent
Нет активного членства (уже независимый)404 IDENTITY_MEMBERSHIP_NOT_ACTIVE (идемпотентно для UI — повтор leave безопасен)
Гонка leave/accept (или leave/исключение)409 CONCURRENCY_CONFLICT (оптимистическая блокировка); побеждает первая зафиксированная, проигравший видит конфликт и перечитывает состояние
agent-membership-changed доставлен потребителю повторно / с меньшей версиейVersion-guard: событие с версией ≤ применённой игнорируется; повтор не удваивает пересчёт
Частичный отказ публикации событийЕдиное согласованное изменение (членство + пользователь + роль + публикация) — либо всё зафиксировано и события уйдут, либо ничего

Согласованность И Идемпотентность

  • Атомарность. Переход членства, отвязка пользователя, переезд роли и постановка событий на публикацию — в одном согласованном изменении; при откате ничего из перечисленного не применяется.
  • Порядок per-agent. Ключ AgentUserId (не MembershipId) гарантирует последовательность join/leave по агенту — потребители видят единственное активное членство консистентно; держат монотонный Version-guard.
  • Идемпотентность. Повторный leave при отсутствии активного членства → 404 IDENTITY_MEMBERSHIP_NOT_ACTIVE (безопасно для UI); потребители дедуплицируют по MessageId.
  • Иммутабельность истории. Пересчёт исторических снапшотов AgencyCompanyId/AgentUserIdзапрещённая операция (ADR-0061 п.4); user-centric метрики/достижения агрегируются по AgentUserId и переживают смену компании.
  • Окно eventual-consistency прав. Между 200 агенту и фактическим снятием прав к ресурсам агентства — задержка обработки user-access-changed (< 5 мин); в окне агент уже «Независимый», но часть прав ещё может действовать до пересчёта.

Acceptance Criteria (Given/When/Then)

  • Happy (выход, метрики сохранены). Дано: у агента активное членство (Status = Active), тип Agent. Когда: агент инициирует «Выйти из агентства». Тогда: членство Active → Left (LeftAt проставлен), CompanyId → null, роль → Platform; опубликованы три события; ростер/метрики/права пересчитаны; исторические сделки/лиды/фиксации не тронуты.
  • Исключение руководителем. Дано: руководитель с identity.users.manage. Когда: исключает агента. Тогда: тот же результат, actor-stamping = руководитель.
  • Уже независимый. Дано: активного членства нет. Когда: повтор leave. Тогда: 404 IDENTITY_MEMBERSHIP_NOT_ACTIVE, состояние неизменно (идемпотентно).
  • Гонка. Дано: одновременно leave и accept/исключение. Когда: обе операции применяются. Тогда: одна фиксируется, другая — 409 CONCURRENCY_CONFLICT.
  • Сохранность истории. Дано: у агента есть завершённые сделки прежней компании. Когда: агент вышел. Тогда: снапшоты AgencyCompanyId/AgentUserId этих сделок не изменились, метрики агента по AgentUserId сохранены.

Source Of Truth И Данные

ДанныеВладелецConsistency rule
AgentMembership / ролиidentityLeft не удаляет запись (история найма); роль переезжает на Platform
Исторические сделки/лиды/фиксацииcrmСнапшоты AgencyCompanyId/AgentUserId иммутабельны, back-fill запрещён
User-centric метрики/достиженияcrm/rewardsАгрегация по AgentUserId (переживает смену компании)
Эффективные праваcatalog/chessboard/gatewayПересчитываются по user-access-changed (eventual)

Контракты И Зависимости

Наблюдаемость

  • critical metrics: число выходов/исключений; консистентность user-centric метрик до/после.
  • trace path: identity (согласованное изменение) → шина событий → crm/rewards/catalog/chessboard.
  • alerts: рассинхрон прав после access-события; пересчёт исторических снапшотов (запрещённая операция).

Риски И Guardrails

  • Риск: потеря наработок агента при развязке. Control: user-centric агрегация по AgentUserId, история найма сохраняется.
  • Риск: «утечка» исторических сделок к независимому агенту. Control: иммутабельные снапшоты компании, back-fill запрещён.
  • Риск: частичное применение. Control: единое согласованное изменение (членство + пользователь + роль + публикация событий).

Обратные ссылки

Автоссылки: где используется этот документ.