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

Profile Service (Pin.Profile)

Назначение

profile владеет личным кабинетом пользователя: расширенный профиль и настройки ЛК, несколько персон на аккаунт (сам/супруг/дети — сделку оформляют на другого человека), документы персон (паспорта РФ/РБ/КЗ/СНГ/иностранные, ИНН, кастомные — значения шифруются на уровне приложения), шаблоны реквизитов для переиспользования в бронировании и сделках CRM, а также заявки на верификацию инвестора (KYC/AML). Контакты учётки (email/phone, ФИО), пароль/пин/2FA остаются в identityprofile хранит их read-only реплику.

Metadata

ПолеЗначение
Статусdraft
Owning teamPIN Platform Core
DomainЛичный кабинет, персональные данные, KYC
Data classificationSensitive (PII: ФИО, даты рождения, паспортные данные — хранятся шифрованными; сканы — в storage)
Runtime criticalitymedium (без profile работают вход/каталог; недоступны ЛК-данные, реквизиты для новых броней и верификация)

Ownership

ArtifactOwner/Rule
PostgreSQL schemaprofile
API areaspublic (self-service ЛК), internal (снапшот реквизитов для crm, m2m), admin (review верификаций — сотрудники ПИН)
Owned entitiesProfileEntity, PersonEntity (+PersonDocumentEntity), RequisiteTemplateEntity, VerificationRequestEntity
Produced eventspin.profile.persons.v1, pin.profile.verification-requests.v1
Consumed eventspin.identity.users.v1
Search indexesnone (списки персон/шаблонов пользователя малы; поиск по шифрованным полям не выполняется)
Analytics factsnone

Ответственность И Границы

  • Делает: профиль ЛК с ограничениями редактирования по типу пользователя; CRUD персон (несколько наборов данных на аккаунт) и их документов с ручным шифрованием значений; typed-модели полей по типу документа; сканы через storage FileId; шаблоны реквизитов + выдача иммутабельных расшифрованных снапшотов для CRM (internal API); заявки верификации инвестора со статусной машиной Draft → Submitted → InReview → Approved/Rejected и очередью ревью.
  • НЕ делает: смену email/phone/ФИО учётки, пароль/пин/2FA, сессии — identity (UC-IDN-004, UC-IDN-002, UC-IDN-003); хранение файлов (storage, presigned-ссылки); документы сделки и их подписание (crm — копирует снапшот реквизитов к себе); уведомления о статусах (notifications по событиям).
  • Source of truth: персоны, документы персон, шаблоны реквизитов, заявки верификации, настройки ЛК, VerificationStatus.
  • Реплики/проекции: реплика полей учётки identity в ProfileEntity (по pin.identity.users.v1); снапшоты реквизитов в сделках — копии, которыми после выдачи владеет crm.

Зависимости

ЗависимостьТипЗачем НужнаFailure mode
PostgreSQL (schema profile)dbВсе агрегаты + outboxПолный отказ сервиса
KafkaqueueConsume pin.identity.users.v1; produce pin.profile.* (outbox)Self-операции работают; реплика identity и уведомления отстают
identityservice (события)Создание/обновление реплики профиля; guard-поля (UserType, ContactsManagedBy, Status)Новые пользователи без профиля до догона consumer-а (retry на 404 профиля)
storageexternal systemЗагрузка/скачивание сканов (presigned-ссылки, FileId)Документы без сканов: загрузка/просмотр сканов недоступны, метаданные работают
handbookservice (internal resolve, m2m)Валидация справочных ссылок на write-path: CountryId документов, CitizenshipCountryId персон (неизвестная/архивная страна → PROFILE_COUNTRY_UNKNOWN); денормализация CountryIsoCode2Fail-closed PROFILE_HANDBOOK_UNAVAILABLE (503): запись стран блокируется, чтение и остальные операции работают (resolve-references.md)
Vault / k8s secretsexternal systemКлючи шифрования персональных данных (AES-256-GCM + HMAC), ротация KeyVersionНедоступна расшифровка/запись шифрованных полей → операции с PII отклоняются (fail-closed)
crmconsumer (m2m)Запрос снапшота реквизитов при оформлении брони/сделкиCRM не может предзаполнить реквизиты; бронь возможна с ручным вводом (fallback CRM)

Проекты

src/services/profile/
Pin.Profile.Api/ # public (ЛК) + internal (снапшот реквизитов) + admin (review KYC)
Pin.Profile.Application/ # прикладная логика операций, guard-ы профиля
Pin.Profile.Domain/ # агрегаты domain/*.md, шифрование доменных VO
Pin.Profile.Infrastructure/ # EF (PostgreSQL), outbox, storage-adapter, crypto key provider
Pin.Profile.Background/ # consumer identity-users-sync; jobs: re-encrypt (ротация ключей), document-expiry-reminder
Pin.Profile.Migrator/ # миграции schema profile
Pin.Profile.Contracts/ # pin.profile.* event contracts

Основные Сущности

EntityНазначениеSource Of TruthДокумент
ProfileEntityПрофиль ЛК: реплика контактов identity, настройки, матрица ограничений по типу пользователя, денормализованный VerificationStatusprofile (реплика-поля — identity)domain/profile.md
PersonEntityПерсона — набор персональных данных (сам/супруг/ребёнок/иное); ФИО и дата рождения шифрованыprofiledomain/person.md
PersonDocumentEntityДокумент персоны: typed-модель по типу (паспорта РФ/РБ/КЗ/СНГ/иностранный, ИНН, кастомный), payload шифрован, сканы — storage FileIdprofiledomain/person-document.md
RequisiteTemplateEntityИменованный набор реквизитов (персона + документы + контакты) для брони/сделокprofiledomain/requisite-template.md
VerificationRequestEntityЗаявка KYC/AML инвестора: иммутабельный шифрованный снапшот + статусная машина + reviewprofiledomain/verification-request.md

ER-обзор

API (карта областей; реестр операций и общие правила — api/README.md)

  • Public self-service (ЛК, /profile/personal): getMyProfile, updateMySettings (Locale/Timezone); персоны — listPersons, createPerson, updatePerson, archivePerson, restorePerson; документы — listPersonDocuments, addPersonDocument, updatePersonDocument, archivePersonDocument, attachDocumentScan/removeDocumentScan (+presigned-выгрузка через storage); шаблоны — listRequisiteTemplates, createRequisiteTemplate, updateRequisiteTemplate, setDefaultTemplate, archiveRequisiteTemplate; верификация — getMyVerification, createVerificationRequest, updateVerificationDraft, submitVerification, revokeVerification. Смена email/phone/ФИО/2FA/пароля — не здесь (identity, ссылки выше).
  • Internal (m2m, потребитель crm): getRequisiteSnapshot(templateId, ownerUserId) — расшифрованный иммутабельный снапшот реквизитов для брони/сделки (+RegisterUsage); getVerificationStatus(userId).
  • Admin (сотрудники ПИН, profile.verification.review): listVerificationQueue (Submitted, FIFO), takeVerificationInReview, approveVerification, rejectVerification, viewVerificationSnapshot (аудируемая расшифровка).

Events И Consumers

TypeNameTopicНазначениеLink
ProducedPersonUpsertedKafkaEventpin.profile.persons.v1Сигнал изменения персоны (без PII: id, relation, status, documentsCount)domain/person.md, контракт — events/person-upserted.md
ProducedVerificationRequestSubmittedKafkaEventpin.profile.verification-requests.v1Заявка в очередь ревью (notifications, support)domain/verification-request.md, контракт — events/verification-request-submitted.md
ProducedVerificationCompletedKafkaEventpin.profile.verification-requests.v1Итог KYC (notifications — инвестору; crm/catalog — бейдж «верифицирован»)domain/verification-request.md, контракт — events/verification-completed.md
ConsumedUserUpsertedKafkaEvent, UserBlockedKafkaEventpin.identity.users.v1Создание/обновление реплики профиля; автосоздание персоны Self инвестораconsumers/identity-users-sync.md, дом события — identity/domain/user.md

Все produced-события публикуются через транзакционный outbox с версионированием контракта (ContractVersion). PII в Kafka не публикуется; передача реквизитов CRM — только синхронный internal API (simplicity-first, ADR-0056).

Кросс-сервисные контракты и согласованность

Как profile встраивается в общий поток данных PIN: что он ожидает от соседей, что предоставляет и с какими окнами согласованности.

Ожидает (входные контракты).

  • От identity — события pin.identity.users.v1 (ключ партиционирования — UserId): поля UserType, ContactsManagedBy, Status, ФИО/контакты учётки. Гарантия: профиль создаётся только по этому событию; до догона консюмера операции ЛК/internal отдают PROFILE_NOT_FOUND (retry). Реплика — read-only; при устаревании guard-поля (ContactsManagedBy, Status) могут временно расходиться с identity в пределах лага (< 1 мин).
  • От crm — синхронный m2m-запрос снапшота реквизитов в момент оформления брони/сделки (не событие).

Предоставляет (исходящие контракты).

  • pin.profile.persons.v1 (ключ — PersonId): изменения персон/документов без PII (id, relation, status, DocumentsCount, version) — потребители держат счётчики/признаки на своей реплике.
  • pin.profile.verification-requests.v1 (ключ — OwnerUserId): итог KYC без PII (Status, RejectionCode, CompletedAt) — notifications уведомляет инвестора, crm/catalog показывают бейдж «верифицирован».
  • Синхронный internal API: расшифрованный снапшот реквизитов (единственный канал PII наружу) и денормализованный VerificationStatus (reconciliation, когда реплики потребителя ещё нет).

Окна согласованности.

  • Profile.VerificationStatus относительно решения по заявке — синхронно (одна транзакция, INV-PRF-6): internal getVerificationStatus опережает или совпадает с push-событием.
  • Бейдж «верифицирован» в crm/catalogeventual (секунды после публикации события).
  • Реплика полей identity в профиле — eventual (< 1 мин, лаг консюмера).
  • Снапшот реквизитов для брони — синхронный; PII в Kafka не публикуется (ADR-0056).

Runtime И Эксплуатация

ОбластьТребование
Health checks/health/live; /health/ready (PostgreSQL, Kafka, доступность ключей crypto); /health/startup (миграции применены)
SLO/SLAself-операции ЛК p99 < 300 мс; internal-снапшот реквизитов p99 < 200 мс (блокирует оформление брони); лаг consumer-а identity < 1 мин
Dashboardprofile-overview (операции ЛК, ошибки расшифровки, очередь верификаций, лаг consumer-а)
Alertsошибки шифрования персональных данных (недоступность ключей) > 0; DLQ identity-sync > 0; заявки в Submitted старше 48 ч; всплеск отказов PROFILE_MANAGED_EXTERNALLY
Runbooksротация ключей шифрования (re-encrypt job по KeyVersion); replay DLQ identity-sync; crypto-erase PII по запросу субъекта (совместно с администратором платформы, ADR-0053)
Feature flagsprofile.verification.enabled (поэтапный запуск KYC)

Безопасность

  • Actor types: user-self (инвестор; агенты/сотрудники — только профиль/настройки), reviewer (сотрудник ПИН, profile.verification.review), care/admin (profile.personal.view-any, аудируемый), m2m (crm — internal-снапшот), system (consumer/jobs).
  • Permission model: identity/domain/permission.md — группа profile.* (personal.view-self/manage-self, contacts.manage-self, documents.view-self/manage-self, requisites.manage-self, verification.submit/review, personal.view-any).
  • Sensitive data classes: ФИО персон, даты рождения, значения документов — только шифротекст (AES-256-GCM, ключи в Vault, KeyVersion для ротации; поиск дублей — HMAC-хэш номера); наружу — только маски (NumberMask); сканы — файлы storage за presigned-ссылками; логи — без значений PII (только id и имена полей).
  • Audit requirements: Version + actor-stamping на всех агрегатах; каждая выдача снапшота CRM и каждая расшифровка снапшота ревьюером — отдельное аудит-событие; KYC-заявки не удаляются.
  • Tenant isolation rule: ADR-0052 — данные инвестора вне tenant; изоляция record-level по OwnerUserId/Id = UserId; кросс-доступ — только явные платформенные permissions (review/заботa).
  • Trusted/untrusted boundary: все входы public API — untrusted (валидация до шифрования); payload событий identity — trusted (внутренний контур); файлы сканов — untrusted (валидация типа/размера на storage + белый список ContentType).

Известные Ограничения И Assumptions

  • Полное удаление PII (right to erasure) — регламент администратора платформы (ADR-0053): crypto-erase ключа записи + очистка storage; в self-service доступен только архив.
  • Контракт storage (upload/presigned) — внешний сервис; здесь фиксируется только граница (FileId, ссылки на скачивание).
  • Верификация — «простая» (ручное ревью сотрудником ПИН); интеграции с внешними KYC/AML-провайдерами нет (добавление провайдера — отдельный ADR).
  • Лимиты (20 персон, 20 документов, 20 шаблонов, 10 сканов по 20 МБ) — simplicity-first (ADR-0056), агрегаты загружаются целиком; пересматриваются по измерениям.
  • Юридические лица как «персона» не поддерживаются (переключатели юр/физ относятся к invest-analytics); при появлении требования — новый тип документа/персоны через ADR.

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