6.2 KiB
Phase 10: Ausschreibungs-Radar Foundation & DÖE Ingestion - Context
Gathered: 2026-07-17 Status: Ready for planning
## Phase BoundaryFundament 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 DecisionsDÖ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-Prefixocds-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 globaleTender-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
fetchals HTTP-Client (kein axios) — sieheicon-discovery.service.ts,ics.provider.ts. - Migrationen als handgeschriebene timestamped Ordner unter
apps/api/prisma/migrations/.
Integration Points
- Neues
TendersModuleself-registriert im Module-Registry. - Globale
Tender-Tabelle (keintenantId); 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>
- „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).
- 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