Контекст: клиент
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 (отдельный) |
| Подписка на изменения identity | Tracium 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 | 🟨 Aggregate | Tracium-extension. Корень: User.Id (наследуется из shared/auth). |
CustomerType | VO enum | individual / legal_entity. |
LegalInfo | VO | Для legal_entity: ИНН, КПП, company_name, registration_country. |
CustomerPricingGroup | E | Тарификация (b2c_retail, b2b_standard, b2b_contract_*, partner_*). |
Contract | E | B2B-договор: набор Price Rules + payment_profile. |
PaymentProfile | VO | card / invoice / prepayment / … |
DeliveryAddress | VO | Адрес доставки. |
CustomerFeature | VO | allow_estimate_approval / require_vat_breakdown / … |
SessionContext | VO | Shared 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 API | Customer | CustomerProfileCreated |
ОбновитьЮридическиеДанные (UpdateLegalInfo) | Customer / Operator | Customer | LegalInfoVerified или LegalInfoVerificationFailed |
НазначитьГруппуТарификации (AssignPricingGroup) | Operator | Customer | CustomerPricingGroupChanged |
ПодписатьДоговор (SignContract) | Operator | Contract | ContractSigned |
ДобавитьАдресДоставки (AddDeliveryAddress) | Customer | Customer | DeliveryAddressAdded |
ПереключитьФичу (ToggleFeature) | Operator / self-service | Customer | CustomerFeatureToggled |
ЗапроситьУдалениеДанных (RequestDataDeletion) | Customer (B2C) | Customer | PersonalDataDeletionRequested (после 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 default | b2c_retail | b2b_standard |
| Контракты | нет | да, multiple |
| 152-ФЗ | applicable | n/a |
| Sessions TTL | политика shared/auth (см. customer-auth.md, расширение TBD) | то же |
| MFA / SSO / OAuth провайдеры / API keys | shared/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) от Moderation | → ApplyModerationDecision + ack |
CustomerPricingGroupChanged | → publish событие → Pricing/Visibility caches invalidate |
user.deleted (shared/auth) + customer_type=individual | → PersonalDataDeletionRequested (старт 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, не дублируется.
Инварианты
Customer.id == User.Id(shared/auth). Без записи в shared/auth — нет Customer’а.customer_type = legal_entity⇒legal_info.innне пусто.customer_pricing_group_refне пуст (иначе дефолт типа клиента).Contract.valid_to ≥ Contract.valid_from.- Tracium никогда не хранит password / TOTP / refresh tokens — это shared/auth.
- Любое
User-поле читается из JWT claims или через shared/auth API; не дублируется в Tracium PG как источник истины (только snapshot вcustomer_directoryдля админки). - Удаление B2C: 30 дней grace после
user.deletedот shared/auth, потом физическое удаление Tracium-extension + ротация DEK supplier_credentials. 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).
| Имя | Когда |
|---|---|
CustomerProfileCreated | Lifecycle |
LegalInfoVerified, LegalInfoVerificationFailed | Compliance |
CustomerPricingGroupChanged, CustomerFeatureToggled | Для Pricing / Visibility |
ContractSigned, ContractExpired | Для Pricing |
CustomerEstimateRequested | Для Ingestion (high-priority refresh) |
PersonalDataDeleted | Compliance |
Подписанные интеграционные события
| Источник | Событие | Реакция |
|---|---|---|
| shared/auth | user.profile.updated | first-touch → CreateCustomerProfile; subsequent → sync read-model |
| shared/auth | user.deleted | start 152-ФЗ grace → PersonalDataDeletionRequested |
| shared/auth | user.messenger.linked / user.messenger.unlinked | UX-уведомления, не меняет Tracium-extension |
| Credentials | SupplierCredentialActivated/Failing/Revoked | Update customer dashboard, notification |
Связи в context map
| BC | Паттерн | Назначение |
|---|---|---|
| shared/auth | Conformist (Tracium ← shared/auth) | Tracium принимает модель User / Role / JwtCustomClaims как есть. Изменения — через PR в shared/auth. |
| Credentials | owns (Customer ссылается) | Lifecycle credentials в Credentials BC |
| Pricing | PL (Customer → Pricing) | CustomerPricingGroupChanged invalidates cache |
| Visibility | SK (Subject, SessionContext) | — |
| Estimate | owns (Estimate.customer_ref) | Customer владеет смет(ами) |
| Search | SK (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
Связанные файлы
- Сценарий:
../scenarios/customer-auth.md,../scenarios/credential-onboarding.md. credentials.md,pricing.md,visibility.md,estimate.md.- shared/auth:
AUTHENTICATION.md,M2M_INTEGRATION.md,SYSTEM_SIGNUP.md,ROLES_INVALIDATION.md,openapi.yaml. ../../40-operations/data-protection.md(TBD).