Files
tessera-ctl/.planning/phases/10-ausschreibungs-radar-foundation-d-e-ingestion/10-CONTEXT.md
T

6.2 KiB

Phase 10: Ausschreibungs-Radar Foundation & DÖE Ingestion - Context

Gathered: 2026-07-17 Status: Ready for planning

## Phase Boundary

Fundament des Ausschreibungs-Radar-Moduls: ein neues, im Marketplace registrierbares Tessera-Modul (wie DKV-Fleet/Cert-Manager), das deutsche Ausschreibungen über die DÖE OpenData-API abruft, in ein einheitliches, plattform-globales (nicht mandantengebundenes) OCDS-orientiertes Schema normalisiert, Änderungen an bestehenden Einträgen erkennt und alles über einen mandantensicheren poll-once-fan-out-many-Scheduler betreibt.

Requirements: CONFIG-01, INGEST-01, INGEST-06, SCHEMA-01, SCHEMA-02.

Nicht in dieser Phase: Filter/UI/Suchprofile (Phase 11), Benachrichtigungen (Phase 12), Scraping-Adapter + Dedup (Phase 13), RSS/E-Mail/i18n-Rollout (Phase 14).

## Implementation Decisions

DÖE-Ingest-Umfang

  • D-01: Initialer Backfill = nur ab jetzt. Beim ersten plattformweiten DÖE-Poll werden nur ab diesem Zeitpunkt veröffentlichte Ausschreibungen aufgenommen — kein historischer Import. Da die Tender-Daten global sind, sieht ein später aktivierender Mandant den seit Plattformstart aufgelaufenen Bestand. Passt zur späteren Backfill-Unterdrückung bei Benachrichtigungen (Phase 12).
  • D-02: Nur offene Ausschreibungen (aktive Vergaben, auf die man bieten kann — tender/contract notices). Vergabeergebnisse, Zuschläge und Aufhebungen (award/result notices) werden in dieser Phase NICHT aufgenommen.
  • D-03: Ingest lädt ganz Deutschland global (plattformweit einmal), keine Vor-Eingrenzung nach Region/CPV beim Ingest — Eingrenzung passiert erst pro Suchprofil (Phase 11). Bewusst wegen Multi-Tenant-Wiederverkauf.
  • D-04: Standard-Poll-Intervall = stündlich, admin-konfigurierbar (INGEST-06).

Datenaufbewahrung

  • D-05: Ausschreibungen nach Ablauf der Abgabefrist werden 90 Tage aufbewahrt, dann gelöscht. Vor Fristablauf: aktiv. Nach Fristablauf: als „abgelaufen" markiert, standardmäßig aus der aktiven Liste ausgeblendet, aber bis zur Löschung recherchierbar.

Claude's Discretion

  • Exakte Prisma-Schema-Felder (OCDS-orientiert, Form gemäß ARCHITECTURE.md), Adapter-Interface (TenderSourceAdapter), Normalizer-Interna, Dedup-Key-Berechnung (OCID → Quelle:NoticeId → …) und contentHash-Änderungserkennung.
  • DÖE-API-Client (native fetch + fast-xml-parser, CSV-Fallback), Pagination/Rate-Limit-Handling (Detail per Phase-Research live zu klären).
  • Scheduler-Implementierung: poll-once-fan-out-many, mehrmandantensicher — NICHT das DKV-findFirst()-Single-Tenant-Muster.
  • Modul-Registrierung im Marketplace nach DKV/Cert-Manager-Vorbild.

<canonical_refs>

Canonical References

Downstream agents MUST read these before planning or implementing.

Milestone-Research (verbindlich)

  • .planning/research/SUMMARY.md — Gesamtbild, Build-Order, Architektur-Kernentscheidungen
  • .planning/research/ARCHITECTURE.md — normalisiertes Schema, TenderSourceAdapter-Abstraktion, global-vs-tenant-Split, Scheduler, Dedup-Strategie, new-vs-modified-Inventar
  • .planning/research/STACK.md — neue Libraries (fast-xml-parser, csv-parse) + „reuse, nicht neu bauen"-Liste
  • .planning/research/PITFALLS.md — Multi-Tenant-Scheduler (Pitfall: DKV-findFirst), Tenant-Isolation in Background-Jobs, OCDS/eForms-Parsing-Fallen
  • .planning/research/ausschreibungs-portale-feasibility.md — DÖE OpenData-API, OCDS-Prefix ocds-mnwr74, Swagger-Endpunkt

Phasen-Vorgaben

  • .planning/ROADMAP.md § Phase 10 — Ziel + 5 Erfolgskriterien (u.a. Zwei-Mandanten-Scheduler-Test)
  • .planning/REQUIREMENTS.md — CONFIG-01, INGEST-01, INGEST-06, SCHEMA-01, SCHEMA-02

Offener Research-Punkt (Phase-Research klären)

  • DÖE OpenData Pagination/Rate-Limit-Parameter — Swagger oeffentlichevergabe.de/documentation/swagger-ui/opendata/ ist JS-gerendert; live gegen die API verifizieren bevor der Poll-Zyklus finalisiert wird. </canonical_refs>

<code_context>

Existing Code Insights

Reusable Assets

  • apps/api/src/dkv/ — Modul-Struktur, Scheduler-Pattern (@nestjs/schedule + SchedulerRegistry) als Vorlage; Mail-/Config-Muster (erst ab Phase 12 relevant).
  • apps/api/src/module-registry/ — Modul-Selbstregistrierung + Marketplace-Aktivierung pro Mandant (CONFIG-01).
  • apps/api/src/prisma/prisma-tenant.extension.ts (forTenant) — Mandanten-Scoping; hier bewusst NUR für tenant-bezogene Tabellen, NICHT für die globale Tender-Tabelle.
  • apps/api/prisma/schema.prisma — Modell-Konventionen (uuid, Timestamps, Migrations-Format).
  • Cert-Manager- und DKV-Modul als Gesamt-Template (Controller/Service/Module/Web-UI-Registrierung).

Established Patterns

  • Native fetch als HTTP-Client (kein axios) — siehe icon-discovery.service.ts, ics.provider.ts.
  • Migrationen als handgeschriebene timestamped Ordner unter apps/api/prisma/migrations/.

Integration Points

  • Neues TendersModule self-registriert im Module-Registry.
  • Globale Tender-Tabelle (kein tenantId); tenant-/user-bezogene Tabellen (Suchprofile, Matches, Triage) kommen in Phase 11.
  • Scheduler läuft plattformweit einmal pro Quelle, unabhängig von der Anzahl aktiver Mandanten. </code_context>
## Specific Ideas
  • „Nur ab jetzt" + „nur offene" + „90-Tage-Retention" zusammen halten die DB schlank und die Trefferliste relevant (nur bietbare, aktuelle Vergaben).
  • Erfolgskriterium aus ROADMAP.md ernst nehmen: Aktivierung für einen zweiten Mandanten darf keinen zweiten DÖE-Poll auslösen (poll-once-fan-out-many).
## Deferred Ideas
  • Vergabeergebnisse/Zuschläge/Aufhebungen (award notices) aufnehmen — spätere Erweiterung, außerhalb v1.1-Scope.
  • Historischer Backfill (letzte 30 Tage / alles) — bei Bedarf später als Konfig-Option.
  • Vor-Eingrenzung des Ingest nach Region/CPV — bewusst verworfen (widerspricht Multi-Tenant-Wiederverkauf); Eingrenzung bleibt Sache der Suchprofile.
  • Aufbewahrungsdauer (90 Tage) später ggf. admin-konfigurierbar machen.

None weiter — Diskussion blieb im Phasen-Scope.


Phase: 10-ausschreibungs-radar-foundation-d-e-ingestion Context gathered: 2026-07-17