ADR-0016: Расширяемые словари kind для графа поставщиков

Status: accepted Date: 2026-04-17 Deciders: команда проекта

Контекст

Если SupplierRole.kind и SupplierRelationship.kind сделать hardcoded enum’ами, любое расширение словаря потребует деплоя. В реальности набор будет расти: появятся специфичные роли (например, “сертифицированный сервис-партнёр”) и новые типы отношений.

Пользователь явно запросил расширяемый словарь.

Альтернативы:

  • A: Hardcoded enum в коде.
  • B: Полностью свободные строки.
  • C: БД-словарь с известным semantic_handler.

Решение

Variant C: БД-словари supplier_role_kind и supplier_relationship_kind с обязательным указанием semantic_handler.

  • Каждый kind — code (машинный) + display_name + semantic_handler + описание.
  • semantic_handler — фиксированный enum в коде (известная семантика: MANUFACTURER, WAREHOUSE_OPERATOR, AGGREGATES, RESELLS, …). Расширение этого enum’а — через ADR.
  • Несколько code могут разделять один semantic_handler (разные нюансы одной семантики).
  • Новый code с semantic_handler = GENERIC — fallback: сохраняется как связь, но не используется в SupplyChainTrace builder.
  • Управление словарями — через admin UI, event-sourced.

Стартовый набор сидируется при инициализации: на старте закладываем 7 ролей и 8 типов отношений.

Обновлённый стартовый набор supplier_relationship_kind:

codesemantic_handlerОписание
aggregatesAGGREGATESАгрегатор (marketplace) опубликовал товар на своей витрине.
resellsRESELLSДистрибьютор перепродаёт товар производителя.
is_agent_ofAGENT_OFАгент заключает сделки от имени производителя/дистрибьютора.
uses_api_ofUSES_API_OFПоставщик использует технический API другого.
uses_warehouse_ofUSES_WAREHOUSE_OFПоставщик хранит товар на чужом складе.
subsidiary_ofSUBSIDIARY_OFЮр-лицо входит в группу.
exclusive_forEXCLUSIVE_FORЭксклюзивный контракт.
listed_onLISTED_ONSeller размещён на агрегаторе (marketplace_seller → marketplace). Добавлен ADR-0026.

Обновлённый стартовый набор supplier_role_kind — включает marketplace_seller (ADR-0026) как легитимную роль в рамках агрегатора.

Supplier-to-Tracium mapping aggregates

Та же схема «расширяемый справочник + фиксированный semantic_handler» применяется к маппингу идентификаторов поставщика на канонические сущности Tracium. Это требуется, потому что каждый поставщик приносит собственные:

  • коды характеристик (ETM: ConfigCharCode / ConfigCharIdVal);
  • коды классификации (ETM: gdsClassTree — 3 уровня);
  • коды производителей (ETM: Info/search/r-manuf(id, value=mnf_code, label)).

Вместо того чтобы тащить это в core, выделяем три aggregate’а в Catalog BC:

AggregateВладелецЧто маппит
SupplierCharacteristicMappingCatalog (со-владение с Enrichment)(supplier_ref, supplier_char_code) → Characteristic.key + unit_override?
SupplierClassificationMappingCatalog(supplier_ref, supplier_class_code) → [ClassificationTag] (multi-tag)
SupplierManufacturerMappingCatalog(supplier_ref, supplier_manufacturer_code) → Manufacturer.id

Правила:

  • Aggregate event-sourced (аудит: кто/когда добавил mapping, как он эволюционировал).
  • Каждая запись имеет confidence + source (declared|imported|inferred) + optional reviewed_by_moderation_case.
  • Отсутствие mapping — валидное состояние. Observer → Matching получает supplier code as-is, попадает в Moderation BC (manufacturer_alias_proposal, новые кейсы characteristic_mapping_proposal, classification_mapping_proposal).
  • Справочник supplier_dictionary_source содержит описание методов, которыми mapping можно пополнять (info_endpoint, sggds_file, manual_csv, ml_inferred).

Warehouse kind

WarehouseKind (введён ADR-0025) ставится в ряд с TrustLevelфиксированный enum, расширяется только через ADR (не admin UI), потому что от него зависит алгоритм lead_time и visibility predicates.

Последствия

Плюсы

  • Гибкость: новый kind заводится в продакшене без деплоя.
  • Безопасность: код всегда знает, как обработать (через semantic_handler), даже неизвестный code.
  • Локализация: display_name можно добавлять без deploy.
  • Audit: история словарей event-sourced.

Минусы

  • Двухуровневая модель (code → semantic) сложнее для разработчика, чем enum.
  • Введение принципиально новой семантики всё равно требует кода (нового semantic_handler).
  • Консистентность данных: при удалении kind из словаря — что делать с существующими entity, его использующими (запрет удаления, archival).

Нейтральные последствия

  • Trust level остаётся фиксированным enum — у него критичная семантика для conflict resolution.

Рассмотренные альтернативы

A: Hardcoded enum

Не удовлетворяет требование расширяемости.

B: Свободные строки без семантики

Потеря безопасности обработки (builder не знает, что делать с новым kind).

Ссылки