Контекст: клиент

NOTE

Статус: Target design. Документ описывает целевую доменную модель. Соответствующий код реализован частично (см. backend/internal/core/) или пока не начат. Правила маркировки — в 50-processes/documentation-standard.md.

Назначение

Хранит бизнес-профиль клиента (B2B и B2C) поверх identity, выдаваемой общим сервисом shared/auth (auth-service). Управляет тарификацией, контрактами, способами оплаты, адресами доставки, бизнес-фичами. Не реализует аутентификацию, JWT, роли, signup, password hash — это shared/auth.

Главный смысл

Customer (Tracium) = User (shared/auth) + Tracium-specific бизнес-поля. Связь по User.Id (UUID). Без записи в shared/auth — нет Customer’а.

Один Customer aggregate, два режима. Различия (документооборот, лимиты, UX) определяются customer_type и customer_pricing_group, не разными моделями.

Граница: что Tracium, что shared/auth

АспектГде живёт
Identity (User.Id, password, email/phone), JWT, refresh, signup, RBAC, MFA, profile basics (Name/Email/Phone), удаление аккаунтаshared/auth
Бизнес-профиль (customer_type, legal_info, customer_pricing_group, contract, payment_profile, delivery_addresses, features)Tracium Customer BC
supplier_credentials[]Tracium Credentials BC (отдельный)
Подписка на изменения identityTracium Customer BC consumer на kafka auth_user_events
Tracium-grace для 152-ФЗ удаленияTracium Customer BC (поверх user.deleted от shared/auth)

Полная механика auth (endpoints, grant types, sessions, расширения) — в ../scenarios/customer-auth.md.

Агрегаты / сущности / value objects

ИмяТипНазначение
Customer🟨 AggregateTracium-extension. Корень: User.Id (наследуется из shared/auth).
CustomerTypeVO enumindividual / legal_entity.
LegalInfoVOДля legal_entity: ИНН, КПП, company_name, registration_country.
CustomerPricingGroupEТарификация (b2c_retail, b2b_standard, b2b_contract_*, partner_*).
ContractEB2B-договор: набор Price Rules + payment_profile.
PaymentProfileVOcard / invoice / prepayment / …
DeliveryAddressVOАдрес доставки.
CustomerFeatureVOallow_estimate_approval / require_vat_breakdown / …
SessionContextVOShared Kernel — composed из JwtCustomClaims (shared/auth: Id, Roles, RolesUpdatedAt) + Tracium fields (customer_type, pricing_group). Строится на API gateway.

User, AuthIdentity, Session, Role, Permission, JwtCustomClaimsв shared/auth, не дублируются здесь. Полный реестр — ../data-model.md.

Доменные события Tracium

Только бизнес-события. Identity-события (user.profile.updated, user.deleted, user.messenger.linked/unlinked) публикует shared/auth — Customer BC их подписан читать.

СобытиеПричина
КлиентРасширенЗарегистрирован (CustomerProfileCreated)Tracium увидел нового User.Id (через auth_user_events или first-touch API request) и создал Tracium-extension
ЮридическиеДанныеПодтверждены (LegalInfoVerified)Verified ИНН (B2B, async job)
ЮридическиеДанныеНеПодтверждены (LegalInfoVerificationFailed)ИНН-verify не прошёл
ГруппаТарификацииНазначена (CustomerPricingGroupChanged)Operator / promo / contract
ДоговорПодписан (ContractSigned)B2B, имеет terms_ref
ДоговорИстёк (ContractExpired)valid_to прошло
КастомнаяФичаПереключена (CustomerFeatureToggled)Operator / self-service
АдресДоставкиДобавлен (DeliveryAddressAdded)Customer / Operator
ЗапросНаУдалениеДанных (PersonalDataDeletionRequested)Tracium-grace, после user.deleted от shared/auth
ПерсональныеДанныеУдалены (PersonalDataDeleted)Конец 30-дневного grace

Команды

КомандаАкторЦелевой агрегатРезультат
СоздатьБизнесПрофиль (CreateCustomerProfile)Policy (auth_user_events) или first-touch APICustomerCustomerProfileCreated
ОбновитьЮридическиеДанные (UpdateLegalInfo)Customer / OperatorCustomerLegalInfoVerified или LegalInfoVerificationFailed
НазначитьГруппуТарификации (AssignPricingGroup)OperatorCustomerCustomerPricingGroupChanged
ПодписатьДоговор (SignContract)OperatorContractContractSigned
ДобавитьАдресДоставки (AddDeliveryAddress)CustomerCustomerDeliveryAddressAdded
ПереключитьФичу (ToggleFeature)Operator / self-serviceCustomerCustomerFeatureToggled
ЗапроситьУдалениеДанных (RequestDataDeletion)Customer (B2C)CustomerPersonalDataDeletionRequested (после shared/auth user.deleted)

Зарегистрироваться / Войти / Выйти / ОбновитьКонтактыкоманды shared/auth, не Tracium.

Различия B2B vs B2C

АспектB2C (individual)B2B (legal_entity)
Регистрацияshared/auth /signup/{phone,email}shared/auth /signup/{phone,email} или /signup/system (M2M) + ИНН-verify (Tracium async)
Документычексчёт, акт, СФ
Pricing group defaultb2c_retailb2b_standard
Контрактынетда, multiple
152-ФЗapplicablen/a
Sessions TTLполитика shared/auth (см. customer-auth.md, расширение TBD)то же
MFA / SSO / OAuth провайдеры / API keysshared/auth (часть TBD-расширений)то же

Политики

ТриггерРеакция
user.profile.updated (shared/auth, первый раз для User.Id)CreateCustomerProfile с дефолтами по типу
user.profile.updated (subsequent)→ sync Name/Email/Phone в read-model customer_directory
CustomerProfileCreated (B2B)→ trigger ИНН-verification job
ИНН-verify вернул ambiguous результат→ publish LegalInfoVerificationEdge → AI-агент legal_info_verification_edge в Moderation BC
ProposedAction(LegalInfoVerified|LegalInfoVerificationFailed) от ModerationApplyModerationDecision + ack
CustomerPricingGroupChanged→ publish событие → Pricing/Visibility caches invalidate
user.deleted (shared/auth) + customer_type=individualPersonalDataDeletionRequested (старт 30-дневного grace)
PersonalDataDeletionRequested + 30 днейDeletePersonalData job + revoke supplier_credentials
ContractSigned→ publish для Pricing
ContractExpired→ revert pricing_group на default + alert customer

Read-модели

  • 🟩 customer_directory (PG) — для админки. Sync с shared/auth по auth_user_events.
  • 🟩 customer_features_view (PG) — flat view фич.
  • 🟩 pending_legal_verifications (PG) — for ops dashboard.

auth_audit (login history) — в shared/auth, не дублируется.

Инварианты

  1. Customer.id == User.Id (shared/auth). Без записи в shared/auth — нет Customer’а.
  2. customer_type = legal_entitylegal_info.inn не пусто.
  3. customer_pricing_group_ref не пуст (иначе дефолт типа клиента).
  4. Contract.valid_to ≥ Contract.valid_from.
  5. Tracium никогда не хранит password / TOTP / refresh tokens — это shared/auth.
  6. Любое User-поле читается из JWT claims или через shared/auth API; не дублируется в Tracium PG как источник истины (только snapshot в customer_directory для админки).
  7. Удаление B2C: 30 дней grace после user.deleted от shared/auth, потом физическое удаление Tracium-extension + ротация DEK supplier_credentials.
  8. SessionContext — immutable VO, строится на API gateway из (JWT claims) + (Customer extension lookup).

Защита персональных данных (152-ФЗ для B2C)

  • Минимизация хранимых данных в Tracium-extension.
  • Шифрование чувствительных полей (адрес доставки, телефон в копии).
  • Удаление по запросу: user.deleted (shared/auth) → 30 дней grace → физическое удаление Tracium-extension.
  • Аудит доступа к ПД в personal_data_access_audit (CH).

Детали — ../../40-operations/data-protection.md (TBD).

Интеграционные события (публикуем)

Топик: customer.events.v1. Partition key: customer_id (= User.Id).

ИмяКогда
CustomerProfileCreatedLifecycle
LegalInfoVerified, LegalInfoVerificationFailedCompliance
CustomerPricingGroupChanged, CustomerFeatureToggledДля Pricing / Visibility
ContractSigned, ContractExpiredДля Pricing
CustomerEstimateRequestedДля Ingestion (high-priority refresh)
PersonalDataDeletedCompliance

Подписанные интеграционные события

ИсточникСобытиеРеакция
shared/authuser.profile.updatedfirst-touch → CreateCustomerProfile; subsequent → sync read-model
shared/authuser.deletedstart 152-ФЗ grace → PersonalDataDeletionRequested
shared/authuser.messenger.linked / user.messenger.unlinkedUX-уведомления, не меняет Tracium-extension
CredentialsSupplierCredentialActivated/Failing/RevokedUpdate customer dashboard, notification

Связи в context map

BCПаттернНазначение
shared/authConformist (Tracium ← shared/auth)Tracium принимает модель User / Role / JwtCustomClaims как есть. Изменения — через PR в shared/auth.
Credentialsowns (Customer ссылается)Lifecycle credentials в Credentials BC
PricingPL (Customer → Pricing)CustomerPricingGroupChanged invalidates cache
VisibilitySK (Subject, SessionContext)
Estimateowns (Estimate.customer_ref)Customer владеет смет(ами)
SearchSK (SessionContext)

Мини event storming

flowchart LR
    AUTH["🌐 shared/auth"]
    K["🟩 kafka auth_user_events"]
    subgraph C["Customer (Tracium)"]
        SUB["🟪 Policy: first-touch User.Id"]
        CMD["🟦 CreateCustomerProfile"]
        AGG["🟨 Customer (extension)"]
        E1["🟧 CustomerProfileCreated"]
        APG["🟪 Policy: assign default pricing_group"]
        CPG["🟧 CustomerPricingGroupChanged"]
        DEL["🟪 Policy: user.deleted + grace 30d"]
        EDEL["🟧 PersonalDataDeleted"]
    end
    subgraph CR["Credentials"]
        OWN["🟪 Customer owns credentials"]
    end
    subgraph P["Pricing"]
        INV["🟪 Invalidate cache"]
    end
    subgraph V["Visibility"]
        SUBJ["🟦 Subject"]
    end

    AUTH -->|user.profile.updated| K
    AUTH -->|user.deleted| K
    K --> SUB --> CMD --> AGG --> E1 --> APG --> CPG
    K -->|user.deleted| DEL --> EDEL
    E1 -.PL.-> OWN
    CPG -.PL.-> INV
    AUTH -.JwtCustomClaims.-> SUBJ

Связанные файлы