docs: complete project research
Ausschreibungs-Radar (v1.1) STACK/FEATURES/ARCHITECTURE/PITFALLS research plus SUMMARY.md synthesis.
This commit is contained in:
+170
-86
@@ -1,112 +1,196 @@
|
||||
# Feature Landscape
|
||||
# Feature Research
|
||||
|
||||
**Domain:** Modular portal platform with marketplace, multi-tenancy, and workflow tool integration
|
||||
**Researched:** 2026-06-18
|
||||
**Domain:** Tender / public-procurement monitoring & aggregation (German "Ausschreibungs-Radar" module, Tessera v1.1)
|
||||
**Researched:** 2026-07-17
|
||||
**Confidence:** MEDIUM-HIGH (domain patterns cross-checked against live tender-monitoring SaaS products — Stotles, Tendium, TenderAlerts, Jorpex — plus OCDS standard docs and the project's own portal feasibility research; German-specific integration details inherit the HIGH confidence already established in `ausschreibungs-portale-feasibility.md`)
|
||||
|
||||
## Table Stakes
|
||||
> **Scope note:** This supersedes the v1.0 platform-level `FEATURES.md` (portal shell, auth, multi-tenancy, marketplace, dashboard widgets) for roadmap purposes — those features already shipped and are not part of this milestone. This document covers ONLY the new Ausschreibungs-Radar tender module.
|
||||
|
||||
Features users expect. Missing = product feels incomplete or unprofessional.
|
||||
## How This Category Works
|
||||
|
||||
Every mature tender-monitoring product (Stotles, Tendium, TenderAlerts, deutsche-eVergabe, vergabe24) follows the same shape, regardless of market:
|
||||
|
||||
1. **Ingest** from N sources (APIs, scrapers, RSS, email alerts) into a **normalized schema**.
|
||||
2. **Deduplicate** across sources — the same tender is frequently published on 2-3 portals plus a central feed (DÖE/TED equivalent).
|
||||
3. Let each user/tenant define **saved-search profiles** (keyword + geography + classification code + deadline + value filters).
|
||||
4. Show a **searchable results list + detail view** linking back to the source portal/documents.
|
||||
5. Track **per-user state** (read/unread, shortlisted/favourite) on top of the shared tender catalog.
|
||||
6. **Notify**: a configurable periodic digest email, plus an instant alert the moment a new tender matches a saved search.
|
||||
|
||||
Tessera's module maps directly onto this shape. The only domain-specific complexity is #1-2 (many incompatible German portal formats, see feasibility doc) — #3-6 are standard SaaS patterns Tessera already has adjacent infrastructure for (DKV inbox ingestion, MailModule/SMTP, NestJS `@nestjs/schedule`, multi-tenant config).
|
||||
|
||||
## Feature Landscape
|
||||
|
||||
### Table Stakes (Users Expect These)
|
||||
|
||||
Features users assume exist. Missing these = product feels incomplete.
|
||||
|
||||
| Feature | Why Expected | Complexity | Notes |
|
||||
|---------|--------------|------------|-------|
|
||||
| User authentication (email/password + admin-created accounts) | Every portal needs login; without it nothing works | Medium | Foundation for all access control |
|
||||
| Role-Based Access Control (RBAC) | Users expect permission boundaries; admins expect control | Medium | Tenant-scoped roles are critical for multi-tenancy |
|
||||
| Multi-tenancy with data isolation | Core requirement per PROJECT.md; customers expect their data is isolated | High | Row-level security (tenant_id on every table) or schema-per-tenant |
|
||||
| Sidebar navigation with categories | Standard portal pattern; users expect hierarchical navigation | Low | Collapsible, categorized by module type |
|
||||
| Module activation/deactivation per tenant | Marketplace without on/off is just a list; tenants expect control | Medium | Admin toggles module visibility and access |
|
||||
| Module licensing (admin-managed) | Core business model; tenants expect clear "you have access to X" | Medium | License = permission to activate; no payment integration yet |
|
||||
| Responsive layout | Web apps must work on different screen sizes | Medium | Desktop-first, but must not break on tablet |
|
||||
| Light/Dark theme | Expected in modern apps; users notice its absence | Low | CSS custom properties + toggle; store preference per user |
|
||||
| Internationalization (i18n) - DE + EN | Core requirement; German users expect German UI | Medium | Must be baked in from day one; retrofitting i18n is painful |
|
||||
| Basic dashboard with widgets | Central landing area gives users a "home" | Medium | Clock, search, notes, calendar as starting widgets |
|
||||
| Session management | Users expect to stay logged in, and to be logged out on timeout | Low | Token-based with configurable expiry |
|
||||
| Error handling and user feedback | Users expect clear feedback on actions (success/error toasts) | Low | Global notification/toast system |
|
||||
| Loading states and skeleton screens | Without them, users think the app is broken | Low | Standard UX pattern |
|
||||
| Search / filter within module lists | Even with 10 modules, users expect to type-to-find | Low | Client-side filter on marketplace and sidebar |
|
||||
| User profile and settings | Users expect to change language, theme, password | Low | Per-user preferences storage |
|
||||
| Central tender ingestion (DÖE OpenData API) | This is the reason the module exists — without it there's no data | MEDIUM | Auth-free eForms-DE/OCDS/CSV, single API, ~75% market value by € — see feasibility doc. No scraping risk. **MVP foundation.** |
|
||||
| Normalized tender schema (OCDS-oriented) | Every source has a different shape; UI/filters need one consistent model | MEDIUM | Store title, buyer, CPV code(s), region/PLZ, deadline, estimated value, procedure type, source URL(s), raw payload |
|
||||
| Full-text keyword search | Users think in free text ("Straßenbau Ludwigsburg"), not structured codes alone | LOW-MEDIUM | Postgres `tsvector`/GIN index on title+description is sufficient at this scale; no need for Elasticsearch |
|
||||
| Region / PLZ / Bundesland filter | German public buyers are geographically scoped; users only want their operating radius | LOW-MEDIUM | PLZ → Bundesland/Landkreis mapping table (static reference data); PLZ range or radius filter |
|
||||
| CPV code / branch filter | CPV (Common Procurement Vocabulary) is the industry-standard classification for tenders — OCDS confirms this is the canonical item classification | MEDIUM | Needs a static CPV code list (~9,400 codes, hierarchical, EU-published) with autocomplete/browse, not just free-text code entry |
|
||||
| Submission deadline filter | Deadline is the single most decision-critical field — "can we even respond in time" | LOW | Simple date range filter; must also drive UI sort/highlighting (see below) |
|
||||
| Estimated contract value min/max filter | Filters out tenders too small/large to be worth pursuing | LOW | Optional field — many Unterschwelle notices omit value; filter must handle NULL gracefully |
|
||||
| Saved-search / filter profiles | Users don't want to re-enter the same 5 filters every day — every competitor product treats "saved search" as the core object, not the raw search | MEDIUM | Per-user (not just per-tenant) — colleagues in the same tenant often watch different branches/regions |
|
||||
| Searchable/sortable results list | Baseline UX for any list-based tool; sort by deadline (most common), value, publish date | LOW-MEDIUM | Standard data-table pattern, consistent with rest of Tessera portal UI |
|
||||
| Detail view with source link + document access | Users must reach the actual Vergabeunterlagen to bid — the module is a radar, not a bidding platform | LOW-MEDIUM | For DÖE/eForms: link to source portal + any document URLs present in the notice; do not attempt to mirror/host bid documents |
|
||||
| Read/unread marking | Universal "inbox" pattern — users triage a daily list of new matches | LOW | Per-user state table, not a tenant-shared flag |
|
||||
| Favourite/shortlist marking | Lets users bookmark tenders they intend to pursue, separate from the read/unread triage state | LOW | Per-user state table; simple boolean + optional "shortlist view" filter |
|
||||
| Periodic email digest | Every native portal alert (see feasibility doc, portals 2,3,4,6,7,8,9,10) already trains users to expect an inbox digest, not "log in and check" | MEDIUM | **Reuses existing `MailModule`/SMTP infra.** Configurable interval (daily/weekly) per saved search or per user, sent via existing scheduler pattern (`dkv-scheduler.service.ts` precedent) |
|
||||
| Instant alert on new match | Deadline-sensitive tenders lose value if a digest arrives after a same-day cutoff was missed; competitors (Stotles, Tendium, native portal Suchprofile) all offer immediate notification as an alternative to digest | MEDIUM | Event-driven: at ingestion time, run new tenders against active saved searches, queue+send immediately. **Reuses `MailModule`.** |
|
||||
|
||||
## Differentiators
|
||||
### Differentiators (Competitive Advantage)
|
||||
|
||||
Features that set Tessera apart from generic portals. Not expected, but valued.
|
||||
Features that set the product apart. Not required, but valuable — align with Tessera's core value ("one platform instead of switching between tools").
|
||||
|
||||
| Feature | Value Proposition | Complexity | Notes |
|
||||
|---------|-------------------|------------|-------|
|
||||
| Drag-and-drop dashboard with resizable widgets | Personal workspace feel; makes dashboard actually useful vs static page | High | Use react-grid-layout or gridstack.js; persist layout per user per tenant |
|
||||
| Widget marketplace/gallery | Users can add widgets to their dashboard from a catalog | Medium | Distinct from module marketplace; lightweight UI components |
|
||||
| Module hot-activation without restart | Modules appear instantly after license grant; no deploy needed | High | Requires dynamic route loading or micro-frontend approach |
|
||||
| LDAP/AD directory sync | Enterprise differentiator; auto-provision users from corporate directory | High | JIT provisioning at login + periodic group sync |
|
||||
| Per-tenant branding/customization | Each tenant gets their own logo, color accent | Medium | Stored in tenant config; CSS variable override |
|
||||
| Module-provided dashboard widgets | Modules can contribute widgets to the dashboard system | Medium | Module manifest declares available widgets; loose coupling |
|
||||
| Activity feed / audit log | Transparency on who did what; compliance-ready | Medium | Event sourcing pattern; filterable by user, action, module |
|
||||
| Admin impersonation ("view as user") | Support tool; admin can see exactly what a user sees | Medium | Scoped session with clear visual indicator |
|
||||
| Keyboard shortcuts and command palette | Power-user acceleration; "Ctrl+K" to jump anywhere | Low | Global listener + fuzzy search over routes and actions |
|
||||
| Onboarding wizard for new tenants | Guides new tenant admins through setup; reduces support burden | Medium | Multi-step flow: branding, users, module selection |
|
||||
| Module dependency declaration | Module A requires Module B; platform enforces this | Low | Manifest-level declaration; block activation if dependency missing |
|
||||
| Notification center | Unified inbox for system events, module alerts, admin messages | Medium | WebSocket or SSE for real-time; persisted read/unread state |
|
||||
| Desktop wrapper (Electron/Tauri) | Installable app feel; taskbar presence, native notifications | Medium | Tauri preferred (smaller binary, Rust-backed) |
|
||||
| Cross-source deduplication with merged source links | The single most-cited "must-have but hard" feature in every tender-aggregator review (Stotles, EU Tenders Monitor) — same Vergabe published on DÖE + AI-AG portal + RSS should appear once, not 3 times | HIGH | No shared ID across sources — needs fuzzy match on buyer+title+CPV+deadline+value within a time window. Only relevant once ≥2 sources are active — **defer past DÖE-only MVP** |
|
||||
| Dashboard widget: upcoming deadlines | Tessera already has a configurable drag-and-drop dashboard with a Calendar widget — an "Ausschreibungen mit nahender Frist" widget reuses that framework directly instead of forcing users into the module | LOW-MEDIUM (once dashboard widget SDK exists) | Direct synergy with the already-built Phase 8 dashboard-widgets work; high leverage, low net-new complexity |
|
||||
| Per-tenant + per-user saved searches with team visibility | Multiple colleagues at one tenant can watch overlapping-but-different regions/branches without duplicating ingestion work | LOW (data-model addition on top of table-stakes saved search) | Natural fit given Tessera's existing multi-tenant architecture |
|
||||
| CSV/Excel export of filtered results | Procurement/BD teams routinely need to hand a tender list to someone outside the tool (management, partner companies) | LOW | Reuse existing export patterns if any exist elsewhere in Tessera; otherwise trivial with the normalized schema |
|
||||
| Relevance ranking within a saved search | Once keyword+CPV+region filters return more than a handful of hits, a simple recency sort undersells partial matches; competitors (Stotles "Signal Score") rank rather than just filter | MEDIUM-HIGH | Defer — only valuable once ingestion volume is large enough that plain filtering feels noisy; not needed for DÖE-only MVP |
|
||||
| "Manual watch" flag for anti-scraping portals (vergabe24, aumass) | Rather than silently having no coverage, the UI can list these portals as "nicht automatisch überwacht — manuell prüfen" with a direct bookmark link, so users aren't surprised by a coverage gap | LOW | Cheap trust-building feature; prevents users assuming full coverage when 2 portals are deliberately excluded per ToS |
|
||||
|
||||
## Anti-Features
|
||||
### Anti-Features (Commonly Requested, Often Problematic)
|
||||
|
||||
Features to explicitly NOT build. These add complexity without proportional value for Tessera's use case.
|
||||
Features that seem good but create problems.
|
||||
|
||||
| Anti-Feature | Why Avoid | What to Do Instead |
|
||||
|--------------|-----------|-------------------|
|
||||
| Third-party module SDK / developer portal | Massive complexity (sandboxing, review pipeline, versioning); only own modules planned | Build a clean internal module contract; open up later if demand arises |
|
||||
| Payment/billing integration | Out of scope per PROJECT.md; adds regulatory and UX burden | Admin-managed license grants; add Stripe/payment only when selling externally |
|
||||
| Real-time collaboration (multiplayer editing) | Workflow tools are typically single-user operations; CRDT/OT is enormously complex | Each module handles its own data; no shared editing state |
|
||||
| AI/ML-powered recommendations | "Suggested modules" adds little value with a small catalog; ML overhead is huge | Manual curation and categories; maybe simple "popular modules" counter later |
|
||||
| Native mobile app | Web-first + desktop wrapper covers the use case; mobile adds two platforms to maintain | Responsive web design; PWA if truly needed later |
|
||||
| Complex workflow orchestration engine (BPMN) | Tessera modules ARE the tools; building a meta-workflow layer is a product in itself | Each module handles its own workflow; cross-module orchestration is future scope |
|
||||
| White-label/full-rebrand per tenant | Different from "per-tenant branding"; full white-label means separate builds, domains, assets | Offer logo + accent color customization; not full theme overhaul per tenant |
|
||||
| Plugin sandboxing (iframe/WASM isolation) | Only own modules are deployed; sandboxing is for untrusted third-party code | Modules are trusted first-party Docker containers; share the same runtime |
|
||||
| Social features (comments, reactions, @mentions) | Not a collaboration tool; adds social complexity without clear workflow value | Keep modules focused on their task; add module-specific notes if needed |
|
||||
| Granular per-field permissions | RBAC at role/module level is sufficient; field-level ACL is enterprise overkill | Role -> Module access mapping; maybe per-module "read/write/admin" tiers |
|
||||
| Feature | Why Requested | Why Problematic | Alternative |
|
||||
|---------|---------------|------------------|-------------|
|
||||
| Scraping vergabe24.de / aumass.de directly | "We already pay for these, just pull the data automatically" | Both portals' AGB explicitly prohibit automated/scripted access; aumass additionally paywalls its alert feature. Legal/ToS risk for a product Tessera intends to resell to customers | Their Oberschwellen notices already flow through DÖE (both are confirmed DÖE data suppliers per feasibility doc); for Unterschwellen coverage, register a native portal Suchprofil manually and ingest the resulting alert emails like any other Unterschwellen source |
|
||||
| Generic "scrape any portal the user pastes a URL for" framework | Feels maximally flexible — "just point it at any Vergabeportal" | German portal landscape has ≥3 incompatible platform families (cosinex, AI-AG NetServer, subreport, bespoke); a generic scraper is either fragile (breaks on every portal redesign) or a permanent maintenance sink. Also raises ToS risk per-portal at scale | Ship a small, fixed set of well-understood adapters (DÖE API, one AI-AG adapter, one cosinex adapter, 2 RSS feeds) as scoped in PROJECT.md; treat any further portal as a deliberate, individually-evaluated addition |
|
||||
| Real-time (sub-hourly) polling of every source | "Instant alert" sounds like it needs constant polling | Tender lifecycles run days-to-weeks; native portal alert emails themselves are typically daily-batch. Sub-hourly polling multiplies scraping load/ToS exposure for near-zero user value | Poll DÖE/RSS on an hourly-to-daily schedule (source-dependent); "instant alert" means instant relative to the last poll/ingestion, not sub-minute real-time |
|
||||
| Full bid-management / CRM (proposal drafting, submission tracking, win/loss pipeline) | "While we're in the tool, why not manage the whole bid lifecycle" | That is a different product category (bid-management software, e.g. Loopio-class tools) — much larger scope, and out of step with the module's stated goal ("durchsuchen, filtern, anzeigen, versenden") | Keep the module a radar: discovery + filtering + notification + shortlisting. Document-heavy bid workflow can be a future, separate module if ever justified |
|
||||
| AI-generated tender summaries / bid-fit scoring | Sounds like a strong differentiator, "let AI tell us if this tender is worth pursuing" | Real value requires per-tenant scoring criteria/training data that doesn't exist yet; premature AI feature adds cost/complexity/hallucination risk before the basic radar has been validated in production | Defer to v2+; if pursued later, treat as its own AI-SPEC-gated phase per Tessera's dev workflow, not part of the v1.1 module |
|
||||
| Mirroring/hosting original Vergabeunterlagen (PDFs) locally | "One-stop shop, don't make users leave the app" | Legal ambiguity around redistributing third-party procurement documents; storage/sync burden keeping mirrors current as portals update documents | Link out to the source portal's document URL; only cache what's needed for parsing (e.g. eForms XML), not full bid document sets |
|
||||
|
||||
## Feature Dependencies
|
||||
|
||||
```
|
||||
Authentication -> RBAC -> Multi-Tenancy (each layer builds on the previous)
|
||||
Multi-Tenancy -> Module Licensing (licenses are tenant-scoped)
|
||||
Module Licensing -> Module Activation (can't activate without license)
|
||||
Module Activation -> Marketplace UI (marketplace displays activation state)
|
||||
Dashboard Framework -> Widget System -> Drag-and-Drop Layout
|
||||
Dashboard Framework -> Module-Provided Widgets (modules contribute to dashboard)
|
||||
i18n Framework -> All UI Components (must be in place before building UI)
|
||||
Theme System -> All UI Components (CSS variables must exist before components)
|
||||
LDAP Integration -> User Management (extends, does not replace manual management)
|
||||
Notification Center -> Module Events (modules emit events to notification system)
|
||||
Sidebar Navigation -> Module Registry (sidebar reflects activated modules)
|
||||
Normalized tender schema (OCDS-oriented)
|
||||
└──requires──> DÖE OpenData ingestion (MVP source)
|
||||
└──enables──> Filter engine (keyword, region, CPV, deadline, value)
|
||||
└──requires──> Saved-search / filter profiles
|
||||
├──requires──> Instant alert (needs Mail infra)
|
||||
└──requires──> Periodic digest (needs Mail infra + Scheduler)
|
||||
|
||||
Read/unread marking ──requires──> per-user tender state table (independent of shared tender catalog)
|
||||
Favourite/shortlist marking ──requires──> per-user tender state table (same table as read/unread)
|
||||
|
||||
Cross-source deduplication ──requires──> ≥2 active ingestion sources
|
||||
(portal adapters or RSS) └──blocks──> "Portal-Adapter (AI-AG/cosinex)" phase
|
||||
└──blocks──> "RSS-Quellen" phase
|
||||
└──blocks──> "E-Mail-Alert-Ingestion" phase
|
||||
|
||||
E-Mail-Alert-Ingestion (Unterschwellen) ──reuses──> DKV inbox infra (ImapProvider / ExchangeInboxProvider)
|
||||
|
||||
Dashboard "upcoming deadlines" widget ──enhances──> Saved-search / filter profiles
|
||||
(reuses existing dashboard widget framework, Phase 8)
|
||||
|
||||
Instant alert ──conflicts-with-if-unthrottled──> Periodic digest
|
||||
(same match sent twice — needs a "already alerted" flag on the per-user tender state
|
||||
so a tender doesn't also appear in the next digest as if new)
|
||||
```
|
||||
|
||||
## MVP Recommendation
|
||||
### Dependency Notes
|
||||
|
||||
Prioritize in this order:
|
||||
- **Filter engine requires the normalized schema, which requires at least the DÖE source to exist:** there is nothing to filter until data is flowing. This is why DÖE-only ingestion is the correct MVP anchor — everything else (saved search, results UI, notifications) can be built and validated against real live data from day one, without waiting on portal-adapter scraping work.
|
||||
- **Cross-source dedup requires ≥2 sources:** building dedup logic against a single source is meaningless (nothing to deduplicate against). This confirms dedup belongs strictly *after* the first portal adapter or RSS source ships, not in the MVP.
|
||||
- **Instant alert and periodic digest both require Mail infra, but need a shared "already notified" state** to avoid double-notifying the same user about the same tender (once instantly, once again in the next digest). This is a small but easy-to-miss data-model requirement — flag for the phase that builds notifications.
|
||||
- **Read/unread and favourite/shortlist state must be per-user, not per-tenant:** the tender catalog itself is shared reference data (a public tender exists independent of which tenant is looking at it), but triage state is personal. This is architecturally different from the DKV module, where nearly everything is tenant-scoped by nature. Worth flagging explicitly for the data-model design in this phase.
|
||||
- **E-Mail-Alert-Ingestion reuses DKV's inbox provider interface** (`apps/api/src/dkv/providers/inbox-provider.interface.ts`, with `ImapProvider`/`ExchangeInboxProvider` implementations) rather than building new inbox-polling code — the DKV module already solved "watch a mailbox, parse structured content out of incoming mail" for a different structured format (fleet PDFs). The tender module needs the same mailbox-watching mechanics, different parser.
|
||||
- **Periodic digest and instant alert reuse the existing `MailModule`/SMTP** (`apps/api/src/mail/mail.module.ts`, `mail.service.ts`) and the scheduler pattern already established by `dkv-scheduler.service.ts` and `ldap-sync.scheduler.ts` — no new mail-sending or cron infrastructure needed, only new templates and trigger logic.
|
||||
|
||||
1. **Authentication + RBAC + Multi-Tenancy** - Foundation; nothing works without it
|
||||
2. **Sidebar navigation + Module registry** - Portal shell; gives the app structure
|
||||
3. **i18n framework (DE + EN)** - Must be first, before building UI text
|
||||
4. **Theme system (light/dark)** - Must be first, before building styled components
|
||||
5. **Marketplace UI with licensing/activation** - Core business logic
|
||||
6. **Basic dashboard with static widgets** - User home; clock, search, notes
|
||||
7. **Domaincheck module** - First real module; validates the entire module architecture
|
||||
8. **Drag-and-drop dashboard** - Differentiator; upgrade from static layout
|
||||
## MVP Definition
|
||||
|
||||
Defer:
|
||||
- **LDAP integration**: High complexity, not needed for initial internal use (manual user creation suffices)
|
||||
- **Desktop wrapper**: Adds build pipeline complexity; browser works fine initially
|
||||
- **Notification center**: Useful but not critical until multiple modules exist
|
||||
- **Admin impersonation**: Support tool; not needed until external customers arrive
|
||||
- **Onboarding wizard**: Only valuable with external tenants; internal users get manual setup
|
||||
### Launch With (v1 / MVP — DÖE-API-only)
|
||||
|
||||
Minimum viable product — validates the whole module concept against real, legally unambiguous, single-source data before any scraping work begins.
|
||||
|
||||
- [ ] DÖE OpenData API ingestion (eForms/OCDS/CSV, auth-free) — the only data source needed to prove the concept
|
||||
- [ ] Normalized tender schema (OCDS-oriented) — required so later sources slot in without a schema rewrite
|
||||
- [ ] Filter engine: keyword full-text, region/PLZ/Bundesland, CPV code, deadline range, value range
|
||||
- [ ] Saved-search / filter profiles (per user, tenant-aware)
|
||||
- [ ] Searchable/sortable UI results list, sortable by deadline/value/publish date
|
||||
- [ ] Detail view with source link + any document URLs present in the DÖE notice
|
||||
- [ ] Read/unread marking (per-user)
|
||||
- [ ] Favourite/shortlist marking (per-user)
|
||||
- [ ] Periodic email digest, interval configurable in the webinterface — reuses `MailModule`
|
||||
- [ ] Instant email alert on new match against an active saved search — reuses `MailModule`
|
||||
|
||||
### Add After Validation (v1.x)
|
||||
|
||||
Features to add once the DÖE-only MVP is live and validated in production use.
|
||||
|
||||
- [ ] AI-AG NetServer portal adapter (covers portals lhs-vpbw, tender24, vergabe.landbw + other AI-AG-hosted portals) — trigger: MVP validated, Unterschwellen coverage gap confirmed as a real pain point
|
||||
- [ ] cosinex VMP adapter (DTVP, reusable across other cosinex-hosted Länder-Marktplätze) — trigger: same as above
|
||||
- [ ] RSS ingestion (subreport-elvis, service.bund.de) — trigger: lowest-effort source expansion, can land alongside or before the scraping adapters
|
||||
- [ ] E-Mail-Alert-Ingestion for remaining Unterschwellen portals, reusing DKV inbox infra — trigger: once ≥1 scraping adapter or RSS source exists, so there's a reason to worry about cross-source overlap
|
||||
- [ ] Cross-source deduplication (fuzzy match on buyer+title+CPV+deadline+value) — trigger: mandatory as soon as a second source goes live, otherwise duplicate tenders will visibly degrade the results list
|
||||
- [ ] "Manual watch" indicator for vergabe24/aumass (excluded portals) — trigger: first user question about "why don't I see X"
|
||||
|
||||
### Future Consideration (v2+)
|
||||
|
||||
Features to defer until the core radar has proven its value.
|
||||
|
||||
- [ ] TED API v3 (EU-wide redundancy) — defer: largely redundant to DÖE for DE-only scope, only relevant if cross-border tenders become a requirement
|
||||
- [ ] Relevance/ranking scoring within saved searches — defer: only needed once match volume per saved search is large enough that plain filtering feels noisy
|
||||
- [ ] Dashboard "upcoming deadlines" widget — defer: high-leverage but depends on prioritizing dashboard-widget-SDK reuse work; not core to validating the radar itself
|
||||
- [ ] CSV/Excel export — defer: cheap to add later, not needed to validate core value
|
||||
- [ ] Team/collaboration features (assign tender to colleague, internal notes) — defer: turns the module toward bid-management scope creep; only pursue if users explicitly ask post-launch
|
||||
|
||||
## Feature Prioritization Matrix
|
||||
|
||||
| Feature | User Value | Implementation Cost | Priority |
|
||||
|---------|------------|----------------------|----------|
|
||||
| DÖE OpenData ingestion + normalized schema | HIGH | MEDIUM | P1 |
|
||||
| Filter engine (keyword/region/CPV/deadline/value) | HIGH | MEDIUM | P1 |
|
||||
| Saved-search profiles | HIGH | MEDIUM | P1 |
|
||||
| Results list + detail view | HIGH | LOW-MEDIUM | P1 |
|
||||
| Read/unread + favourite marking | MEDIUM | LOW | P1 |
|
||||
| Periodic digest + instant alert (via MailModule) | HIGH | MEDIUM | P1 |
|
||||
| AI-AG NetServer adapter | MEDIUM-HIGH | HIGH | P2 |
|
||||
| cosinex DTVP adapter | MEDIUM | MEDIUM-HIGH | P2 |
|
||||
| RSS ingestion (subreport, service.bund.de) | MEDIUM | LOW-MEDIUM | P2 |
|
||||
| E-Mail-Alert-Ingestion (Unterschwellen) | MEDIUM | MEDIUM (reuses DKV infra) | P2 |
|
||||
| Cross-source deduplication | HIGH (once ≥2 sources) | HIGH | P2 |
|
||||
| Dashboard "upcoming deadlines" widget | MEDIUM | LOW-MEDIUM | P3 |
|
||||
| Relevance ranking | LOW-MEDIUM | MEDIUM-HIGH | P3 |
|
||||
| CSV/Excel export | LOW-MEDIUM | LOW | P3 |
|
||||
| Team/collaboration (notes, assignment) | LOW | MEDIUM-HIGH | P3 |
|
||||
|
||||
**Priority key:**
|
||||
- P1: Must have for launch (DÖE-only MVP)
|
||||
- P2: Should have, add when possible (portal-adapter / multi-source phases)
|
||||
- P3: Nice to have, future consideration
|
||||
|
||||
## Competitor Feature Analysis
|
||||
|
||||
| Feature | Native German portals (DTVP, subreport, deutsche-eVergabe) | Modern SaaS aggregators (Stotles, Tendium, TenderAlerts) | Tessera's approach |
|
||||
|---------|--------------------------------------------------------------|-----------------------------------------------------------|---------------------|
|
||||
| Saved search + alert | Yes — "Suchprofil" per portal, daily email | Yes — core object of the product | Yes — single unified saved-search model across all sources, not one per portal |
|
||||
| Multi-source coverage | No — each portal only shows its own listings | Yes — 50-1,000+ sources aggregated centrally | Yes, phased: DÖE (central feed) → 2 portal adapters → RSS → email-ingestion for the long tail |
|
||||
| Deduplication | N/A (single source) | Yes — explicitly marketed as a core feature (Stotles, EU Tenders Monitor) | Deferred to v1.x, once ≥2 sources are live (see dependency notes) |
|
||||
| Read/unread + shortlist | Rare — most native portals only offer a flat list | Yes — standard inbox-style triage pattern | Yes, in MVP — per-user state table |
|
||||
| Deadline-aware notification | Digest only, no distinction between digest and instant | Some distinguish instant vs. digest | Both instant alert and periodic digest, user-configurable, in MVP |
|
||||
| Bid-management/CRM add-ons | No | Some upsell into full bid-management (Stotles) | Explicitly out of scope (anti-feature) — module stays a radar |
|
||||
|
||||
## Sources
|
||||
|
||||
- [WorkOS: Multi-tenant RBAC design](https://workos.com/blog/how-to-design-multi-tenant-rbac-saas)
|
||||
- [Logto: Build a multi-tenant SaaS application](https://logto.medium.com/build-a-multi-tenant-saas-application-a-complete-guide-from-design-to-implementation-d109d041f253)
|
||||
- [Cloudscape Design System: Configurable Dashboard](https://cloudscape.design/patterns/general/service-dashboard/configurable-dashboard/)
|
||||
- [FreeCodeCamp: Type-safe plugin architecture in React](https://www.freecodecamp.org/news/how-to-design-a-type-safe-lazy-and-secure-plugin-architecture-in-react/)
|
||||
- [Backstage.io: Plugin-based developer portal](https://backstage.io/)
|
||||
- [AppMaster: Audit logging for internal tools](https://appmaster.io/blog/audit-logging-internal-tools-activity-feed)
|
||||
- [Frontegg: SaaS Multitenancy components](https://frontegg.com/blog/saas-multitenancy)
|
||||
- [Medium: Node.js Plugin Architecture with ES Modules](https://medium.com/codeelevation/node-js-plugin-architecture-build-your-own-plugin-system-with-es-modules-5b9a5df19884)
|
||||
- [DevelopersVoice: Plugin-ready modular monolith](https://developersvoice.com/blog/dotnet/building_plugin_ready_modular_monolith/)
|
||||
- [Gridstack.js: Interactive dashboards](https://gridstackjs.com/)
|
||||
- `.planning/research/ausschreibungs-portale-feasibility.md` — project-internal, HIGH confidence (already-verified portal-by-portal capability matrix, native alert features, ToS constraints, DÖE/TED coverage stats)
|
||||
- [Open Contracting Data Standard — Codelists & Schema Reference](https://standard.open-contracting.org/latest/en/schema/codelists/) — CPV classification usage, deadline/timezone handling, currency-attached values (MEDIUM-HIGH, official standard docs)
|
||||
- [Open Contracting Data Standard — official site](https://www.open-contracting.org/data-standard/) — schema/building-blocks overview (MEDIUM-HIGH)
|
||||
- [Stotles — Tender Alerts & Tracker](https://www.stotles.com/platform/track-tenders) — unified feed from 1,000+ portals, Signal Score relevance ranking, saved searches (MEDIUM, vendor marketing but consistent with independent sources)
|
||||
- [Tendium — Tender Monitoring](https://tendium.ai/en/tender-monitoring/) — monitoring/alert pattern confirmation (MEDIUM)
|
||||
- [TenderAlerts.eu](https://tenderalerts.eu/) — EU tender aggregation, notification dashboard pattern (MEDIUM)
|
||||
- [EU Tenders Monitor (Apify)](https://apify.com/nicolas_izquierdo/eu-tenders-monitor) — explicit confirmation that "relevance scoring, deduplication and new-only alerts" are treated as a standard feature set for this category (MEDIUM)
|
||||
- [deepbloo — What Is a Tender Monitoring Platform](https://deepbloo.com/blog-posts/what-is-a-tender-monitoring-platform-a-complete-guide-to-tools-for-public-procurement-intelligence) — category definition, deduplication described as critical (MEDIUM)
|
||||
- Codebase inspection: `apps/api/src/dkv/providers/inbox-provider.interface.ts`, `dkv-scheduler.service.ts`, `apps/api/src/mail/mail.service.ts`, `apps/api/src/mail/mail.module.ts` — confirms reusable infra for email-ingestion, scheduling, and outbound mail (HIGH — direct codebase read)
|
||||
|
||||
---
|
||||
*Feature research for: German public-procurement tender monitoring/aggregation module (Ausschreibungs-Radar)*
|
||||
*Researched: 2026-07-17*
|
||||
|
||||
Reference in New Issue
Block a user