Profile Service (Pin.Profile)
Назначение
profile владеет личным кабинетом пользователя: расширенный профиль и настройки ЛК, несколько персон
на аккаунт (сам/супруг/дети — сделку оформляют на другого человека), документы персон (паспорта
РФ/РБ/КЗ/СНГ/иностранные, ИНН, кастомные — значения шифруются на уровне приложения), шаблоны реквизитов
для переиспользования в бронировании и сделках CRM, а также заявки на верификацию инвестора (KYC/AML).
Контакты учётки (email/phone, ФИО), пароль/пин/2FA остаются в identity — profile хранит их read-only
реплику.
Metadata
| Поле | Значение |
|---|---|
| Статус | draft |
| Owning team | PIN Platform Core |
| Domain | Личный кабинет, персональные данные, KYC |
| Data classification | Sensitive (PII: ФИО, даты рождения, паспортные данные — хранятся шифрованными; сканы — в storage) |
| Runtime criticality | medium (без profile работают вход/каталог; недоступны ЛК-данные, реквизиты для новых броней и верификация) |
Ownership
| Artifact | Owner/Rule |
|---|---|
| PostgreSQL schema | profile |
| API areas | public (self-service ЛК), internal (снапшот реквизитов для crm, m2m), admin (review верификаций — сотрудники ПИН) |
| Owned entities | ProfileEntity, PersonEntity (+PersonDocumentEntity), RequisiteTemplateEntity, VerificationRequestEntity |
| Produced events | pin.profile.persons.v1, pin.profile.verification-requests.v1 |
| Consumed events | pin.identity.users.v1 |
| Search indexes | none (списки персон/шаблонов пользователя малы; поиск по шифрованным полям не выполняется) |
| Analytics facts | none |
Ответственность И Границы
- Делает: профиль ЛК с ограничениями редактирования по типу пользователя; 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 | Полный отказ сервиса |
| Kafka | queue | Consume pin.identity.users.v1; produce pin.profile.* (outbox) | Self-операции работают; реплика identity и уведомления отстают |
identity | service (события) | Создание/обновление реплики профиля; guard-поля (UserType, ContactsManagedBy, Status) | Новые пользователи без профиля до догона consumer-а (retry на 404 профиля) |
| storage | external system | Загрузка/скачивание сканов (presigned-ссылки, FileId) | Документы без сканов: загрузка/просмотр сканов недоступны, метаданные работают |
handbook | service (internal resolve, m2m) | Валидация справочных ссылок на write-path: CountryId документов, CitizenshipCountryId персон (неизвестная/архивная страна → PROFILE_COUNTRY_UNKNOWN); денормализация CountryIsoCode2 | Fail-closed PROFILE_HANDBOOK_UNAVAILABLE (503): запись стран блокируется, чтение и остальные операции работают (resolve-references.md) |
| Vault / k8s secrets | external system | Ключи шифрования персональных данных (AES-256-GCM + HMAC), ротация KeyVersion | Недоступна расшифровка/запись шифрованных полей → операции с PII отклоняются (fail-closed) |
crm | consumer (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, настройки, матрица ограничений по типу пользователя, денормализованный VerificationStatus | profile (реплика-поля — identity) | domain/profile.md |
PersonEntity | Персона — набор персональных данных (сам/супруг/ребёнок/иное); ФИО и дата рождения шифрованы | profile | domain/person.md |
PersonDocumentEntity | Документ персоны: typed-модель по типу (паспорта РФ/РБ/КЗ/СНГ/иностранный, ИНН, кастомный), payload шифрован, сканы — storage FileId | profile | domain/person-document.md |
RequisiteTemplateEntity | Именованный набор реквизитов (персона + документы + контакты) для брони/сделок | profile | domain/requisite-template.md |
VerificationRequestEntity | Заявка KYC/AML инвестора: иммутабельный шифрованный снапшот + статусная машина + review | profile | domain/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
| Type | Name | Topic | Назначение | Link |
|---|---|---|---|---|
| Produced | PersonUpsertedKafkaEvent | pin.profile.persons.v1 | Сигнал изменения персоны (без PII: id, relation, status, documentsCount) | domain/person.md, контракт — events/person-upserted.md |
| Produced | VerificationRequestSubmittedKafkaEvent | pin.profile.verification-requests.v1 | Заявка в очередь ревью (notifications, support) | domain/verification-request.md, контракт — events/verification-request-submitted.md |
| Produced | VerificationCompletedKafkaEvent | pin.profile.verification-requests.v1 | Итог KYC (notifications — инвестору; crm/catalog — бейдж «верифицирован») | domain/verification-request.md, контракт — events/verification-completed.md |
| Consumed | UserUpsertedKafkaEvent, UserBlockedKafkaEvent | pin.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): internalgetVerificationStatusопережает или совпадает с push-событием.- Бейдж «верифицирован» в
crm/catalog— eventual (секунды после публикации события). - Реплика полей
identityв профиле — eventual (< 1 мин, лаг консюмера). - Снапшот реквизитов для брони — синхронный; PII в Kafka не публикуется (ADR-0056).
Runtime И Эксплуатация
| Область | Требование |
|---|---|
| Health checks | /health/live; /health/ready (PostgreSQL, Kafka, доступность ключей crypto); /health/startup (миграции применены) |
| SLO/SLA | self-операции ЛК p99 < 300 мс; internal-снапшот реквизитов p99 < 200 мс (блокирует оформление брони); лаг consumer-а identity < 1 мин |
| Dashboard | profile-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 flags | profile.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.
Связанные Документы
- Домены: profile · person · person-document · requisite-template · verification-request.
- Соседние контексты: identity (контакты/2FA/пароль, события users),
docs/06-services/crm/(потребитель снапшотов реквизитов),docs/06-services/notifications/,docs/06-services/support/(очередь ревью KYC) — по будущим ТЗ. - Решения: ADR-0052 (tenancy), ADR-0053 (админы/удаление PII), ADR-0056 (simplicity-first); technical-assignment.md, service-boundaries.md, glossary.md.