Offers
NOTE
Статус: Live (HEAD
5e4ce54, 2026-05-06). Read-model для supplier-offers; live-fetch UPSERT добавлен в Live-B/C/D циклах. Admin read endpoint работает.
Offers BC хранит наблюдения по предложениям поставщиков (SupplierOffer aggregate) и поддерживает write-port для live-fetch warmup из других BC. Это не канонический товар — это снимки конкретных позиций конкретных поставщиков с привязкой к credential, по которой получены.
Назначение
Хранить per-(offer × credential × seller × observed_at) снимки price/stock/delivery от поставщиков. Source-of-truth для proposal pipeline на read-side и для downstream BC (matching, charnorm).
Статус документа
- Тип знания:
current service - Статус реализации:
SupplierOfferaggregate + read repo + live-write port (ObservationWriterдля Live-B/C cache warm) + admin read endpoint. - Текущее место кода:
backend/internal/core/offers/{api,app,domain,infra}/. - Что читать дальше:
../../20-architecture/adr/0025-price-and-stock-observation-extensions.md,../../20-architecture/adr/0026-marketplace-observations-and-seller-axis.md.
Область действия
Входит:
SupplierOfferaggregate (offerID, supplier_ref, supplier_code, manufacturer, etc).- Observation read repo — основной потребитель ingestion (write через
offers/app). ObservationWriterport — Live-B пишет fresh price; Live-C пишет stock/delivery.- Admin endpoint
/api/v1/admin/offers/supplier-offers.
Не входит:
- Канонический товар (catalog/canonical).
- Matching offer ↔ canonical (matching BC).
- Visibility filtering (visibility BC применяется на proposal-edge).
Публичный контракт
Вход
HTTP (admin):
GET /api/v1/admin/offers/supplier-offers— list по фильтру (supplier, manufacturer, code).
Internal Go API:
OffersWriter.UpsertObservation(...)— пишут ingestion + Live-B/C.OffersReader.GetByID(...)/.ListByCanonical(...)/ etc — для matching, proposal.ObservationWriter.UpsertLivePrice(...)/UpsertLiveStock(...)— Live-B/C cache warm.
Выход
- Outbox events
offer.observation.v1(наполняется ingestion’ом или live-fetch warm).
Внутренняя архитектура
domain/offer.go—SupplierOfferaggregate + IDs (OfferID).domain/value_objects.go—Observation,WarehouseStock,DeliveryWindow.domain/ports.go— Reader + Writer + ObservationWriter (live-write).domain/repository.go— domain port.app/UpsertObservation— main use case.infra/pg/— Repo + ON CONFLICT logic per Live-B unique key(offer_id, credential_ref, seller_ref, observed_at)сNULLS NOT DISTINCT.
Зависимости
- PostgreSQL —
supplier_offer+supplier_offer_observation. - catalog/canonical — read-only (через mapping в matching BC).
Хранилище
| Таблица | Назначение |
|---|---|
supplier_offer | aggregate metadata (supplier_ref + supplier_code key) |
supplier_offer_observation | per-observation snapshot (price/stock/delivery + observed_at + credential_ref + seller_ref) |
outbox_events | publish |
Конфигурация
Per-BC env vars не требуются (всё через PG pool из api-server).
Тестирование
- Unit:
go test ./internal/core/offers/... - Integration:
-tags=integrationдля Repo (UNIQUE с NULLS NOT DISTINCT specifics).
Наблюдаемость
- Metrics:
offers_observations_written_total{source="ingestion|live"},offers_observations_read_total{purpose}. - Phase logs:
offers.upsert.phase.
Открытые вопросы / TODO
- Public read endpoint
/v1/offers/{id}(target spec, не активирован).
Связанные документы
- ADR:
0025,0026. - Соседи:
../ingestion/README.md,../matching/README.md,../catalog-core/README.md.