---
title: "Durrani Website — Plan (Phase 2, Architektur)"
type: project
tags: [durrani, website, plan, architecture, nextjs, payload, kadi, seo, security]
date: 2026-05-24
status: draft
source: "[[spec]]"
related: "[[spec]], [[constitution]], [[durrani-platform-evaluation-2026-05-24]]"
---

# Plan: Durrani Website (Phase 2)

> Architektur-Plan zu [[spec]], geprüft gegen [[constitution]]. Status: Draft, vor Gate B (Kais-Review).

## 1. Technical Context

| Layer | Wahl | Begründung |
|---|---|---|
| Frontend | **Next.js (App Router, latest) — SSG + ISR** | Statisch = max Speed/Security/AI-HTML; ISR = CMS-Updates ohne Full-Rebuild |
| Frontend-Hosting | **Cloudflare Pages** (Empfehlung) o. Vercel | Edge-CDN + WAF + DDoS + günstig; Frontend hält keine personenbez. Daten (gehen an KADi/Payload-APIs) → kein EU-Daten-Problem im Frontend. *Decision D1 unten.* |
| CMS | **Payload 3** (self-hosted, Netcup-VPS) + **Postgres** | EU-Daten-Souveränität, schöne deutsche Editor-UI, i18n + Localization built-in, code-defined Collections |
| Media | Payload Media → Objektspeicher + **CDN davor**, AVIF/WebP | „extrem optimiert", kein CLS |
| i18n | Payload-Localization + **next-intl** Routing (`/[locale]/…`) | DE primär, EN sekundär, hreflang |
| Reservierung | **KADi-API (tief)** server-side + eigene UI | Constitution VII, Premium-Erlebnis, Showcase |
| Bestellung | GloriaFood-Embed, consent-/consentfrei | bleibt v1 |
| Analytics | **Umami self-hosted (Netcup)** | cookieless → kein Banner, DSGVO-first |
| CI/CD | GitHub Actions → Lighthouse-CI + axe + Playwright + Schema-Check, Deploy on green | Performance-Budget als Merge-Gate |

## 2. Architecture Decisions (mit Rationale)

- **AD-1 Headless-Split:** Payload+Postgres+Media auf **Netcup-VPS** (EU, Daten-Souveränität, Constitution IV), Next-Frontend auf **Cloudflare/Vercel** (Edge-Performance/Security). Frontend zieht Content build-/ISR-time via Payload-API. Webhook Payload→Next triggert ISR-Revalidate bei Content-Änderung.
- **AD-2 SSG-first, ISR für Dynamik:** Seiten statisch vorgerendert; Speisekarte/Events/Öffnungszeiten via ISR (revalidate on webhook). Reservierungs-Verfügbarkeit = client/server-fetch live von KADi (nicht statisch).
- **AD-3 Multi-Location als First-Class-Datenmodell, single-location-Ausspielung v1:** alle ortsbezogenen Collections tragen `location`-Relation; Routing intern location-aware, v1 rendert nur Gelnhausen ohne Location-Segment in der URL — späterer Standort = Daten + Route, kein Re-Architecting (Constitution X).
- **AD-4 KADi-Integration server-side:** Next-Route-Handler (BFF) ruft KADi-API mit Server-Credentials (Secret nie im Client); eigene Reservierungs-UI rendert Verfügbarkeit + postet Buchung. Entkoppelt Frontend von KADi-Internals + schützt Keys.
- **AD-5 Consentless-Analytics:** Umami cookieless → kein Cookie-Banner für Analytics. GloriaFood/KADi nur bei Bedarf consent-gaten (prüfen ob deren Embeds cookies setzen).
- **AD-6 Kein RPC-Wildwuchs:** interne BFF-Endpoints RESTful, Errors RFC-7807. KADi-Calls gekapselt in einem `kadi`-Client-Modul (1 Stelle bei API-Änderung).

## 3. Constitutional Compliance Check

| Principle | Plan erfüllt? |
|---|---|
| I. Security-First | ✓ statisch, kein PHP/DB-public, Cloudflare-WAF, CMS-Admin nicht öffentlich+2FA, Turnstile, Security-Header |
| II. Performance-Budget | ✓ SSG/ISR + Edge + Image-Pipeline; Lighthouse-CI als Merge-Gate (LCP<2s, ≥95) |
| III. SEO+AI | ✓ Next-Metadata + JSON-LD je Seite, Sitemap, semantisches SSG-HTML |
| IV. EU-Daten-Souveränität | ✓ Payload+DB+Media Netcup/EU; Frontend ohne PII-Speicherung |
| V. Editor-Autonomie | ✓ Payload-UI, deutsche Felder, Preview, kein Content-in-Code |
| VI. Kein Auto-Publish | ✓ Auto-SEO nur Draft→Review (Pipeline später) |
| VII. KADi-First | ✓ AD-4 tiefe eigene Integration |
| VIII. Premium+A11y | ✓ WCAG-AA in Test-Strategy (axe), Premium-UX-Design Phase |
| IX. Keine Über-Komplexität | ✓ Standard-Stack (Next+Payload), keine exotischen Deps; Split begründet |
| X. Extensibility-ready | ✓ AD-3 Multi-Location, Catering-Collection, Auth-fähiges Payload für Roadmap |
| XI. Mobile-First | ✓ Test-Strategy mobil-zuerst, Design mobil-zuerst |

## 4. Data Model (Payload Collections — Multi-Location-ready)

- **Locations** (v1: 1×Gelnhausen): name, slug, address, geo (lat/lng), NAP, openingHours[], contact, social.
- **MenuCategories**: title (localized DE/EN), order, location→.
- **MenuItems**: name (localized), description (localized), price, allergens[], dietary (halal/veg/vegan flags), image→Media, category→, location→.
- **Pages**: slug, locale, flexible Block-Content (Hero, Text, Galerie, CTA, FAQ…).
- **Events**: title (localized), date/range, description, image, location→.
- **Catering**: offerings (localized) — eigene Collection (Channel-ausbaubar).
- **Reviews**: optional, oder live aus Google.
- **Media**: Bilder (auto AVIF/WebP-Derivate, alt-Text localized).
- **Globals**: BrandSettings (Logo, Farben, Fonts), SEO-Defaults, NAP, Social.
- **Users**: CMS-Editoren (Rollen: Admin/Editor), 2FA.

## 5. API Contracts

- **Payload** REST/GraphQL — Content-Fetch build/ISR + Preview.
- **Revalidate-Webhook** Payload→Next (`POST /api/revalidate`, signed) bei Publish.
- **KADi** (BFF-gekapselt, Contract TBD — `[NEEDS CLARIFICATION: KADi-API]`): `GET availability(date, partySize)`, `POST booking`, `GET booking/status`, Auth-Modell, ggf. Webhook. → blockt nur KADi-Deep-Task.
- **GloriaFood** — Client-Embed, kein Server-Contract.
- Interne Errors: RFC-7807.

## 6. File-Structure (Frontend-Repo, geplant)

```
durrani-web/ (KADiCon-Repo)
├── app/[locale]/(site)/...        # Routen: home, speisekarte, reservierung, events, catering, kontakt, about, legal
├── app/api/revalidate, app/api/kadi/*   # BFF
├── components/  (ui, sections, reservation, menu)
├── lib/  (payload-client, kadi-client, i18n, schema-jsonld)
├── messages/  de.json en.json
└── tests/  (unit, e2e Playwright, lighthouse-ci config)
payload/ (separates Netcup-Deployment: collections, payload.config, Dockerfile)
```

## 7. Test Strategy
- **Unit** Vitest (lib: schema-builder, kadi-client, price/i18n-utils).
- **Component** RTL (reservation-form, menu-list).
- **E2E** Playwright: P1-Flows (Speisekarte rendern, Reservierung end-to-end gegen KADi-Sandbox, Bestell-CTA), mobil-Viewport zuerst.
- **Perf** Lighthouse-CI gegen Budgets (Merge-Gate).
- **A11y** axe + Keyboard-Walk (WCAG-AA).
- **Schema** Rich-Results-Validierung im CI.

## 8. Migration / Rollout
1. Build auf Staging-Subdomain (z.B. `new.restaurant-durrani.de`).
2. Content: Texte neu (DE+EN), gute Bestandsfotos übernehmen + optimieren.
3. **301-Redirects** alte WP-URLs (`/speisekarte`, `/kontakt`, `/weihnachtsfeier`, `/2468-2/`…) → neue Routen (SEO-Erhalt, FR-040).
4. DNS-Cutover auf neues Hosting; alte WP sichern + abschalten (xmlrpc-Surface weg).
5. Google Search Console: neue Sitemap, Redirects prüfen, Rich Results validieren; Google Business Profile NAP-Sync.
6. Monitoring: Lighthouse-CI, Umami, Uptime.

## 9. Quickstart-Validation (nach Build)
1. `pnpm build` grün + Lighthouse-CI ≥ Budgets.
2. Speisekarte im CMS ändern → erscheint nach ISR-Revalidate live.
3. Reservierung end-to-end gegen KADi-Sandbox → Bestätigung.
4. Rich Results Test: alle Schema-Typen 0 Fehler.
5. axe-Scan 0 kritische Verstöße, Mobile-Lighthouse ≥95.

## 10. Decisions — ENTSCHIEDEN (Gate B, Kais 2026-05-24)
- **D1 Frontend-Hosting: Cloudflare Pages.** Security/Edge/Kosten/geringe Angriffsfläche > Next-Bequemlichkeit. → Next via `@cloudflare/next-on-pages` bzw. statischer Export + ISR-Äquivalent (Revalidate via Deploy-Hook/Cron). Image-Optimierung über Cloudflare Images.
- **D2 Menu-Quelle: Payload führend.** KADi-Menu-API nur optional später für Sync/Vergleich. Editor-Autonomie + SEO-Kontrolle + AI-lesbare CMS-Struktur haben Vorrang.
- **D3 KADi-Integration: Discovery-First + Mock-Adapter.** Repo `github.com/KADiCon-UG/kadicon.git` (API noch nicht final dokumentiert). → eigener Discovery-Task (siehe unten), Mock-Adapter erlaubt Frontend-Bau gegen stabiles Interface bevor die echte API steht.
- **D4 Brand-Sprache: Aria-Vorschlag in Design-Phase.** Logo behalten (fix); Farben/Typografie/Bildsprache/Komponenten/Premium-Look separat reviewen (eigenes DESIGN.md + Review-Gate).

## 11. KADi-Discovery & Integration (D3-Detail, Kais-Vorgabe)
Eigener Task-Cluster, Server-only, keine API-Keys im Browser:
1. KADi-Repo lesen → **API-Endpunkte identifizieren**.
2. **Auth-Modell klären** (Token/Session/tenant-Scoping via `tenant-id`).
3. **Availability-Endpoint** definieren (date, partySize → freie Slots).
4. **Booking-Endpoint** definieren (create reservation → Bestätigung/ID).
5. **Webhooks** definieren (Status-Updates Reservierung).
6. **Rate-Limits / Bot-Schutz** definieren.
7. **Server-only Integration** (BFF, Keys serverseitig).
8. **Mock-Adapter** bauen (gleiches Interface wie echte API) → Frontend baubar bevor API stabil; später 1 Stelle umstellen.
