Доменная модель

NOTE

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

Это index доменной области Tracium. Здесь — карта Bounded Contexts, отношения между ними и Big Picture event storming (хронология ключевых событий).

Для глубокого погружения в конкретный контекст — contexts/. Для сквозных бизнес-сценариев — scenarios/. Для справочника моделей данных (агрегаты, сущности, value objects) — data-model.md.

Обозначения Event Storming

Используется классическая нотация. Обязательно к использованию во всех контекстных и сценарийных файлах.

СтикерЧто обозначаетMarkdown-обёртка
🟧 Domain EventФакт, произошёл в прошлом. Past tense.> [!event]
🟦 CommandНамерение/императив. Что-то должно произойти.> [!command]
🟨 AggregateГраница консистентности, обработчик команд.> [!aggregate]
🟪 PolicyРеакция: «когда событие X — отправить команду Y».> [!policy]
🟩 Read Model / ViewПроекция для чтения, UI, поиска.> [!readmodel]
🟫 ActorЧеловек или внешняя система, инициирующая команду.> [!actor]
🔴 HotspotОткрытый вопрос, конфликт, точка модерации.> [!hotspot]
🌐 External SystemВнешняя система (API поставщика, KMS, …).> [!external]

Доменные события именуются на русском языке в past tense (НаблюдениеЗаписано). В скобках — машинное имя для маппинга в код и Kafka-топики (OfferObservationRecorded).

Список Bounded Contexts

BCНазначениеФайл
Catalog (Каталог)Canonical Product, identity, characteristics, equivalencecontexts/catalog.md
Ingestion (Поступление данных)Опрос поставщиков, маршрутизация credentials, raw payloads, EnrichmentJobcontexts/ingestion.md
Offers (Предложения поставщиков)SupplierOffer (товарная канва), SupplierOfferObservation (append-only)contexts/offers.md
Matching (Сопоставление)Anti-Corruption Layer между Offers и Catalog: MatchDecision, orphan pool, rematchingcontexts/matching.md
Enrichment (Обогащение)AI/LLM-извлечение характеристик и контентаcontexts/enrichment.md
Pricing (Ценообразование)Pricing Engine, Price Rules, Custom Pricing Handlerscontexts/pricing.md
Visibility (Видимость данных)DataVisibilityPolicy enginecontexts/visibility.md
Moderation (Модерация — AI)AI-агенты для всех кейсов, требовавших оператора (match_review, orphan_resolution, inferred relationships, critical attr enrichment, price overlap, credential group, ИНН edge). Человек только при escalation.contexts/moderation.md
Search (Поиск)Поисковый индекс, аналоги, ранжированиеcontexts/search.md
Estimate (Смета)Estimate aggregate, оптимизацияcontexts/estimate.md
Customer (Клиент)Customer, Pricing Group, Contract, B2B/B2C аутентификацияcontexts/customer.md
Credentials (Учётные записи)SupplierCredential, CredentialGroup, fingerprint, lifecyclecontexts/credentials.md
Supplier Network (Сеть поставщиков)Граф поставщиков, роли, связи, SupplyChainTracecontexts/supplier-network.md

Context Map

flowchart LR
    classDef core fill:#fef3c7,stroke:#b45309,color:#1c1917
    classDef supporting fill:#dbeafe,stroke:#1d4ed8,color:#1c1917
    classDef generic fill:#e5e7eb,stroke:#4b5563,color:#1c1917
    classDef external fill:#fae8ff,stroke:#a21caf,color:#1c1917

    SUPPLIER[("Внешний API поставщика")]:::external
    KMS[("KMS / Vault")]:::external
    USER[("Пользователь B2B/B2C")]:::external
    LLM[("Внешний LLM провайдер")]:::external

    Customer["Customer<br/>(B2B/B2C, auth)"]:::supporting
    Credentials["Credentials"]:::supporting
    Ingestion["Ingestion"]:::core
    Offers["Offers"]:::core
    Matching["Matching<br/>(ACL)"]:::core
    Catalog["Catalog"]:::core
    SupplierNet["Supplier Network"]:::supporting
    Enrichment["Enrichment"]:::supporting
    Pricing["Pricing"]:::core
    Visibility["Visibility"]:::supporting
    Search["Search"]:::supporting
    Estimate["Estimate"]:::core
    Moderation["Moderation<br/>(AI-агенты)"]:::supporting
    CPH["Custom Pricing Handler<br/>(plugin)"]:::generic

    USER -- "регистрация / login" --> Customer
    Customer -- "owns (1..N)" --> Credentials
    Credentials -- "auth_payload (encrypted)" --> KMS

    Ingestion -- "uses CredentialContext (OHS)" --> Credentials
    Ingestion -- "fetch (REST/feed/webhook)" --> SUPPLIER
    Ingestion -- "publishes RawPayloadStored<br/>OfferObservationRecorded (PL)" --> Offers
    Ingestion -- "uses graph для trust/route" --> SupplierNet

    Offers -- "OfferSeen / OfferUpdated → Match (ACL)" --> Matching
    Matching -- "MatchDecided → CanonicalCreated/Linked (PL)" --> Catalog
    Catalog -- "CanonicalCharacteristicChanged (PL)" --> Enrichment
    Enrichment -- "CharacteristicEnriched (PL)" --> Catalog

    SupplierNet -- "SupplyChainTrace (Shared Kernel VO)" --> Offers
    SupplierNet -- "SupplyChainTrace (SK)" --> Pricing
    SupplierNet -- "SupplyChainTrace (SK)" --> Visibility

    Pricing -- "select observation" --> Offers
    Pricing -- "uses CredentialContext + group" --> Credentials
    Pricing -- "trigger when API нет ценовых правил" --> CPH
    CPH -- "PricingAdjustment" --> Pricing
    Pricing -- "filter / transform" --> Visibility

    Search -- "Conformist: read projections" --> Catalog
    Search -- "Conformist" --> Offers
    Search -- "filter" --> Visibility

    Estimate -- "use case orchestrator" --> Search
    Estimate -- "use case orchestrator" --> Pricing
    Estimate -- "use case orchestrator" --> Matching
    Estimate -- "filter" --> Visibility

    Matching -- "ModerationItemAdded / OrphanItemAssigned (PL)" --> Moderation
    Enrichment -- "EnrichmentReviewRequired (PL)" --> Moderation
    SupplierNet -- "InferredRelationshipProposed (PL)" --> Moderation
    Pricing -- "PriceRuleConflictDetected (PL)" --> Moderation
    Credentials -- "CredentialGroupCandidateOpened (PL, без секретов)" --> Moderation
    Catalog -- "ManufacturerAliasProposalCreated (PL)" --> Moderation
    Customer -- "LegalInfoVerificationEdge (PL)" --> Moderation
    Moderation -- "ProposedAction → ApplyModerationDecision (PL)" --> Matching
    Moderation -- "ProposedAction (PL)" --> Catalog
    Moderation -- "ProposedAction (PL)" --> Enrichment
    Moderation -- "ProposedAction (PL)" --> SupplierNet
    Moderation -- "ProposedAction (PL)" --> Credentials
    Moderation -- "ProposedAction (PL)" --> Customer
    Moderation -- "sandboxed LLM call (ACL)" --> LLM

    USER -- "search / estimate / order" --> Search
    USER -- "upload estimate" --> Estimate

Легенда отношений

DDD-паттерны, использованные на карте:

СокращениеПаттернЧто означает
PLPublished LanguageКонтекст публикует стабильную схему интеграционных событий (Kafka). Подписчики читают как контракт.
ACLAnti-Corruption LayerКонтекст переводит «грязную» внешнюю модель в свою чистую. Matching изолирует Catalog от Offers.
OHSOpen Host ServiceКонтекст предоставляет стабильный API для нескольких потребителей (Credentials → Ingestion/Pricing).
SKShared KernelМаленький общий kernel из VO (например, SupplyChainTrace). Изменяется по согласованию всех владельцев.
ConformistПодписчик принимает модель публикатора как есть (Search → Catalog).
Customer/SupplierUpstream диктует, downstream подстраивается. Принимаются изменения через переговоры, не вето.

Big Picture — хронология (Event Storming Timeline)

Сквозная лента доменных событий, упорядоченная во времени по 4 фазам. Каждое событие принадлежит одному BC (помечено меткой). Подробности — в файлах контекстов.

Фаза 1 — Onboarding клиента и подключение поставщика

#СобытиеBCТриггер
1КлиентЗарегистрирован (CustomerRegistered)CustomerActor: Visitor → команда ЗарегистрироватьКлиента
2КлиентАутентифицирован (CustomerAuthenticated)CustomerActor: Customer → Войти
3УчётнаяЗаписьПоставщикаПередана (SupplierCredentialProvided)CredentialsCustomer → ДобавитьУчётнуюЗапись
4УчётнаяЗаписьЗашифрована (CredentialEncryptedAtRest)CredentialsPolicy: при SupplierCredentialProvided — обернуть DEK через KMS
5УчётнаяЗаписьПроверена (SupplierCredentialValidated)CredentialsPolicy: тестовый запрос через коннектор
6ГруппаУчётныхЗаписейОбъединена (SupplierCredentialGroupMerged)CredentialsPolicy: совпал fingerprint
7ПолитикаВидимостиПрименена (VisibilityPolicyAttachedToSubject)VisibilityPolicy: при КлиентЗарегистрирован — применить дефолты pricing_group

Подробно: scenarios/customer-auth.md, scenarios/credential-onboarding.md.

Фаза 2 — Поступление данных от поставщика и сборка каталога

#СобытиеBCТриггер
8ЗапросНаПоступлениеДанныхСоздан (EnrichmentJobEnqueued)IngestionSchedule / user request / system event
9СырыеДанныеПолучены (RawPayloadStored)IngestionConnector → S3
10ТоварнаяКанваОбнаружена (SupplierOfferSeen)OffersParser извлёк (supplier_ref, supplier_sku)
11НаблюдениеЗаписано (OfferObservationRecorded)OffersParser → append-only observation
12СовпадениеЗапрошено (MatchAttemptRequested)MatchingPolicy: при SupplierOfferSeen или OfferCharacteristicsUpdated
13СовпадениеРешено (MatchDecided)MatchingMatcher: exact / strong / probable / weak / unmatched
14КаноническийТоварСоздан (CanonicalCreated)CatalogPolicy: при MatchDecided=unmatched + valid identity_signature
15ХарактеристикаДобавлена (CharacteristicAdded)CatalogPolicy: новая critical attribute из offer
16ХарактеристикаОбогащенаAI (CharacteristicEnrichedByAI)EnrichmentPolicy: при отсутствии critical attr запустить LLM
17ЦепочкаПоставокПересчитана (SupplyChainTraceRecomputed)Supplier NetworkPolicy: при SupplierOfferSeen или изменении графа
18ОчередьМодерацииПополнена (ModerationItemAdded)Matching / Catalog / Pricing / Credentials / SupplierNet / Enrichment / CustomerЛюбая ситуация, требовавшая бы оператора → input для AI-агента Moderation BC
18aКейсМодерацииСоздан (ModerationCaseCreated)ModerationPolicy: при любом ModerationItemAdded
18bСобраныСвидетельства (EvidenceGathered)ModerationPre-LLM: read-only сбор данных из source BC
18cАгентВынесРешение (AgentDecisionEmitted)ModerationLLM в sandbox вернул verdict + confidence
18dРешениеПримененоВИсходномBC (DecisionApplied)Source BC (Matching / Catalog / …)Применение ProposedAction через ack-loop
18eКейсЭскалирован (CaseEscalated)ModerationТолько при low confidence / security-critical / circuit broken

Подробно: scenarios/ingestion-flow.md, scenarios/matching-flow.md.

Фаза 3 — Обновления и определение разницы (diff)

#СобытиеBCТриггер
19ПовторныйОпросЗапланирован (RefreshScheduled)IngestionCron / TTL
20НаблюдениеЗаписано (новое) (OfferObservationRecorded)OffersAppend-only
21ИзменениеЦеныОбнаружено (OfferPriceChanged)Offers (derived)Diff к предыдущему observation
22ИзменениеОстатковОбнаружено (OfferStockChanged)Offers (derived)Diff
23ИзменениеХарактеристикОбнаружено (OfferCharacteristicsUpdated)OffersDiff в товарной канве
24СтатусЖизненногоЦиклаОбновлён (OfferLifecycleStatusChanged)OffersПоставщик пометил discontinued
25КаноническийТоварУстаревший (CanonicalDeprecated)CatalogPolicy: все active offers стали discontinued
26ПреемникУстановлен (ReplacedBySet)CatalogManual / supplier feed
27ПовторныйМатчингЗапущен (RematchRequested)MatchingPolicy: при OfferCharacteristicsUpdated или новых aliases
28СовпадениеУлучшено (MatchUpgraded)Matchingprobable → strong / strong → exact

Подробно: scenarios/update-and-diff.md.

Фаза 4 — Поиск, цена, смета (клиентский путь)

#СобытиеBCТриггер
29СметаЗагружена (EstimateUploaded)EstimateCustomer → ЗагрузитьСмету
30СтрокаСметыРаспознана (EstimateLineParsed)EstimateParser (LLM-assisted)
31КандидатыНайдены (SearchCandidatesReturned)SearchEstimate → НайтиКандидатов
32КандидатВыбран (EstimateLineCandidateSelected)EstimateCustomer / auto если match_confidence ≥ strong
33РасчётЦеныЗапрошен (PricingRequested)PricingEstimate → РассчитатьЦену
34ИсточникНаблюденияВыбран (PricingObservationSelected)PricingАлгоритм: own credential > group > system
35ПолитикаВидимостиПрименена (VisibilityPolicyEvaluated)VisibilityPolicy: фильтр / transform
36ПравилоЦеныПрименено (PriceRuleApplied)PricingЦепочка по priority
37КастомныйОбработчикВызван (CustomPricingHandlerInvoked)PricingPolicy: API поставщика не вернул правил → плагин клиента
38ЦенаРассчитана (PricingResolved)PricingС breakdown + observation_source
39СметаОптимизирована (EstimateOptimized)EstimateILP / greedy
40СметаЗафиксирована (EstimateFinalized)EstimateCustomer подтвердил

Подробно: scenarios/analog-search.md, scenarios/pricing-calculation.md, scenarios/estimate-end-to-end.md.

Big Picture — диаграмма (mermaid)

flowchart TB
    subgraph Phase1["Фаза 1 — Onboarding"]
        E1["🟧 КлиентЗарегистрирован"]
        E2["🟧 КлиентАутентифицирован"]
        E3["🟧 УчётнаяЗаписьПередана"]
        E5["🟧 УчётнаяЗаписьПроверена"]
        E6["🟧 ГруппаОбъединена"]
        E1 --> E2 --> E3 --> E5 --> E6
    end

    subgraph Phase2["Фаза 2 — Каталог"]
        I1["🟧 СырыеДанныеПолучены"]
        I2["🟧 ТоварнаяКанваОбнаружена"]
        I3["🟧 НаблюдениеЗаписано"]
        I4["🟧 СовпадениеРешено"]
        I5["🟧 КаноническийТоварСоздан"]
        I6["🟧 ЦепочкаПоставокПересчитана"]
        I1 --> I2 --> I3 --> I4 --> I5
        I3 --> I6
    end

    subgraph Phase3["Фаза 3 — Diff"]
        U1["🟧 ПовторныйОпросЗапланирован"]
        U2["🟧 НаблюдениеЗаписано (new)"]
        U3["🟧 ИзменениеЦеныОбнаружено"]
        U4["🟧 ИзменениеХарактеристикОбнаружено"]
        U5["🟧 ПовторныйМатчингЗапущен"]
        U1 --> U2 --> U3
        U2 --> U4 --> U5
    end

    subgraph Phase4["Фаза 4 — Клиентский путь"]
        C1["🟧 СметаЗагружена"]
        C2["🟧 СтрокаРаспознана"]
        C3["🟧 КандидатыНайдены"]
        C4["🟧 РасчётЦеныЗапрошен"]
        C5["🟪 Policy: API без правил?"]
        C6["🟧 КастомныйОбработчикВызван"]
        C7["🟧 ЦенаРассчитана"]
        C8["🟧 СметаОптимизирована"]
        C1 --> C2 --> C3 --> C4 --> C5
        C5 -->|да| C6 --> C7
        C5 -->|нет| C7
        C7 --> C8
    end

    Phase1 -.-> Phase2
    Phase2 -.-> Phase3
    Phase2 -.-> Phase4
    Phase3 -.-> Phase4

Принципы снижения связности (decoupling)

Система спроектирована так, чтобы изменение в одном BC не каскадно ломало другие. Правила:

  1. Нет прямых вызовов между Core BC — общение только через интеграционные события Kafka (Published Language). Синхронные in-process вызовы допустимы лишь внутри одного процесса (например, Estimate → Pricing внутри estimate-builder).
  2. Anti-Corruption Layer на границах — Matching изолирует Catalog от любых форм supplier-данных. Любые поля поставщика парсятся в собственную модель Offers, оттуда — в Matching, и только потом — в Catalog.
  3. Shared Kernel минимизирован — общий kernel: SupplyChainTrace, Money, CredentialContext, Subject. Любое изменение — RFC + согласие всех владельцев.
  4. Open Host Service для plugin-точек — Custom Pricing Handler регистрируется через Open Host без знания внутренностей Pricing.
  5. Eventual consistency между BC — invariant внутри агрегата строгий, между BC — eventually consistent. Идемпотентность consumer’ов обязательна (см. ../20-architecture/event-sourcing.md).
  6. Read-проекции вместо запросов — Search не дёргает Catalog API; читает свою проекцию, обновляемую consumer’ом из Kafka.
  7. No-proxy — клиент не дожидается поставщика синхронно. Pricing graceful degrades на stale observation, ставит async EnrichmentJob (см. ../20-architecture/integration-patterns.md).
  8. AI-модерация по умолчанию — все ручные кейсы (match review, orphan resolution, inferred relationships, critical attr review, price overlap, credential group merge, ИНН edge) идут в Moderation BC и обрабатываются AI-агентами в детерминированном sandbox. Человек подключается только при escalation (low confidence / security-critical / circuit breaker). Source BC не имеет ручного UI — только Moderation BC может выписать кейс на человека.

Сценарии (end-to-end)

СценарийФайл
Поступление данных от поставщика → хранилищеscenarios/ingestion-flow.md
Обновления + diff-detection + rematchingscenarios/update-and-diff.md
Новый offer → каноническое сопоставлениеscenarios/matching-flow.md
Подбор аналоговscenarios/analog-search.md
Подключение клиентской credential поставщикаscenarios/credential-onboarding.md
Регистрация / login B2B и B2C клиентовscenarios/customer-auth.md
Расчёт цены (с custom handlers)scenarios/pricing-calculation.md
Полный путь сметы (Acts 1–8)scenarios/estimate-end-to-end.md
AI-модерация (автоматическая обработка очереди)scenarios/ai-moderation-flow.md

Связанное