# GraphQL Middlelayer + Astro Frontend Ein moderner, skalierbarer Onlineshop mit GraphQL-basiertem Middlelayer und Astro-Frontend. Der Middlelayer fungiert als flexible Abstraktionsschicht zwischen Frontend und verschiedenen Datenquellen. ## 🚀 Features - **GraphQL API** mit Apollo Server v5 - **Astro Frontend** mit SSR und Client-Side Interaktivität - **Adapter Pattern** für flexible Datenquellen (Mock, Headless CMS, Database) - **Performance-Optimierungen**: - Dataloader für Batch-Loading (verhindert N+1 Queries) - Redis/In-Memory Caching - Response Caching - Query Complexity Limits - **Monitoring & Observability**: - Structured Logging (Winston) - Prometheus Metrics - Distributed Tracing - **Type-Safe** mit TypeScript durchgängig - **Modern UI** mit Tailwind CSS und Alpine.js - **Internationalization (i18n)**: - URL-basierte Locales (`/de`, `/en`) - Übersetzungen aus Middlelayer (mit Default-Fallback) - CMS-Inhalte mehrsprachig (vorbereitet für Contentful) ## 📋 Voraussetzungen - Node.js 18+ - npm oder yarn - (Optional) Redis für verteiltes Caching ## 🛠️ Installation ```bash # Dependencies installieren npm install # (Optional) Redis installieren (macOS) brew install redis brew services start redis ``` ## 🏃 Quick Start ```bash # GraphQL Server + Astro Frontend starten npm start # Oder einzeln: npm run mock:server # Nur GraphQL Server (Port 4000) npm run dev # Nur Astro Frontend (Port 4321) ``` ## 📁 Projektstruktur ``` / ├── middlelayer/ # GraphQL Middlelayer │ ├── adapters/ # Datenquellen-Adapter (Mock, CMS, etc.) │ ├── config/ # Konfiguration │ ├── monitoring/ # Logging, Metrics, Tracing │ ├── plugins/ # Apollo Server Plugins │ ├── types/ # TypeScript Typen │ ├── utils/ # Utilities (Cache, Dataloader, etc.) │ ├── index.ts # Server Entry Point │ ├── schema.ts # GraphQL Schema │ └── resolvers.ts # GraphQL Resolvers │ ├── src/ # Astro Frontend │ ├── components/ # Astro Komponenten │ ├── layouts/ # Layout Templates │ ├── lib/ # Utilities & GraphQL Client │ ├── pages/ # Astro Pages │ └── styles/ # Global Styles │ └── docs/ # Dokumentation ``` ## ⚙️ Konfiguration ### Environment Variables Erstelle eine `.env` Datei im Root-Verzeichnis: ```env # Server Ports PORT=4000 # GraphQL Server Port METRICS_PORT=9090 # Metrics Server Port # Logging LOG_LEVEL=info # debug, info, warn, error NODE_ENV=development # development, production # GraphQL MAX_QUERY_COMPLEXITY=1000 # Query Complexity Limit # Redis (Optional) REDIS_ENABLED=true # Redis Cache aktivieren REDIS_HOST=localhost # Redis Host REDIS_PORT=6379 # Redis Port REDIS_PASSWORD= # Optional: Redis Password # Cache TTLs (in Millisekunden) CACHE_PAGES_TTL=60000 # 60 Sekunden CACHE_PAGE_SEO_TTL=300000 # 5 Minuten CACHE_NAVIGATION_TTL=300000 # 5 Minuten CACHE_PRODUCTS_TTL=30000 # 30 Sekunden ``` ### Cache-Konfiguration Cache-TTLs können über Environment Variables oder direkt in `middlelayer/config/cache.ts` konfiguriert werden. ## 🌐 Endpoints | Service | URL | Beschreibung | |---------|-----|--------------| | GraphQL API | `http://localhost:4000` | GraphQL Endpoint & Playground | | Metrics | `http://localhost:9090/metrics` | Prometheus Metrics | | Health Check | `http://localhost:9090/health` | Service Health Status | | Astro Frontend | `http://localhost:4321` | Frontend Application | ## 📊 GraphQL Schema ### Queries ```graphql # Produkte query { products(limit: 4) { id name price originalPrice promotion { category text } } product(id: "prod-123") { id name description price } } # CMS (mit Locale-Support) query { pageSeo(locale: "de") { title description } page(slug: "/about", locale: "de") { headline subheadline } pages(locale: "en") { slug name } navigation(locale: "de") { links { name slug } } } # Übersetzungen query { translations(locale: "de", namespace: "auth") { locale translations { key value } } } } page(slug: "/") { slug name headline } navigation { name links { name url } } } ``` ## 🏗️ Architektur ### Middlelayer Der Middlelayer verwendet das **Adapter Pattern** für flexible Datenquellen: ``` Frontend (Astro) ↓ GraphQL API (Apollo Server) ↓ DataService (Singleton) ↓ DataAdapter (Interface) ↓ ┌─────────────┬──────────────┬─────────────┐ │ Mock Adapter│ CMS Adapter │ DB Adapter │ └─────────────┴──────────────┴─────────────┘ ``` **Vorteile:** - Einfaches Wechseln zwischen Datenquellen - Testbarkeit durch Mock-Adapter - Zentrale Logik im DataService - Caching auf Service-Ebene ### Frontend - **Astro** für Server-Side Rendering - **Alpine.js** für Client-Side Interaktivität - **Tailwind CSS** für Styling - **GraphQL Client** für Datenabfragen ## 🔧 Entwicklung ### NPM Scripts ```bash npm run mock:server # Startet GraphQL Server npm run dev # Startet Astro Dev Server npm start # Startet beide (concurrently) npm run build # Production Build npm run preview # Preview Production Build ``` ### Neuen Adapter hinzufügen 1. Implementiere `DataAdapter` Interface: ```typescript // middlelayer/adapters/myAdapter.ts import type { DataAdapter } from './interface.js'; export class MyAdapter implements DataAdapter { async getProducts(limit?: number): Promise { // Implementierung } // ... weitere Methoden } ``` 2. In `middlelayer/adapters/config.ts` registrieren: ```typescript export function createAdapter(): DataAdapter { const adapterType = process.env.ADAPTER_TYPE || 'mock'; if (adapterType === 'myAdapter') { return new MyAdapter(); } // ... } ``` ## 📈 Monitoring ### Prometheus Metrics Metriken sind verfügbar unter `http://localhost:9090/metrics`: - `graphql_queries_total` - Anzahl der Queries - `graphql_query_duration_seconds` - Query-Dauer - `cache_hits_total` / `cache_misses_total` - Cache-Statistiken - `dataservice_calls_total` - DataService-Aufrufe - `errors_total` - Fehler-Anzahl ### Logging Structured Logging mit Winston: - Console Output (Development) - JSON Logs (Production) - Log-Level konfigurierbar ### Tracing Automatische Trace-ID-Generierung für Request-Tracking. ## 🚀 Deployment ### Production Build ```bash npm run build ``` ### Docker (Beispiel) ```dockerfile FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --production COPY . . RUN npm run build CMD ["npm", "start"] ``` ## 🧪 Testing ```bash # GraphQL Query testen curl -X POST http://localhost:4000 \ -H "Content-Type: application/json" \ -d '{"query":"{ products(limit: 4) { id name price } }"}' # Health Check curl http://localhost:9090/health # Metrics curl http://localhost:9090/metrics ``` ## 📚 Weitere Dokumentation - [Architektur & Skalierung](./docs/ARCHITECTURE_SCALING.md) - Detaillierte Architektur-Analyse - [Redis Setup](./middlelayer/utils/REDIS_SETUP.md) - Redis Cache Konfiguration - [Monitoring](./middlelayer/monitoring/README.md) - Monitoring & Observability ## 🐛 Bekannte Probleme ### Query Complexity Schema-Realm-Konflikt **Problem:** `graphql-query-complexity` hat einen Schema-Realm-Konflikt mit Apollo Server. **Workaround:** Das Plugin überspringt den Check bei Realm-Konflikten automatisch. Funktioniert, aber Complexity-Check wird manchmal übersprungen. **Status:** Funktioniert, permanente Lösung in Arbeit. ## 🌍 Internationalization (i18n) ### URL-basierte Locales Das System unterstützt URL-basierte Locales: - `/de` - Deutsche Version - `/en` - Englische Version - `/` - Auto-Redirect zu Browser-Locale oder Cookie ### Übersetzungen **Architektur:** - **Defaults**: Fallback-Übersetzungen in `src/lib/i18n/defaults.ts` - **Middlelayer**: Übersetzungen können vom GraphQL-Server geladen werden - **Überschreibungen**: Middlelayer-Übersetzungen überschreiben Defaults **Verwendung in React:** ```tsx import { useI18n } from "../lib/i18n/useI18n.js"; function MyComponent() { const { t } = useI18n("auth"); return

{t("login.title")}

; } ``` **Verwendung in Alpine.js:** ```html
``` ### CMS-Locale-Support **Contentful-Ansatz:** Contentful verwendet ein Locale-System, bei dem: - Jedes Content-Feld lokalisiert werden kann - Standard-Locale wird definiert (z.B. `en-US`) - Fallback-Locales können konfiguriert werden - API-Aufrufe enthalten `locale`-Parameter: `fields.headline['en-US']` **Unser System:** - GraphQL-Schema unterstützt `locale`-Parameter für alle CMS-Queries - Adapter-Interface ist vorbereitet für Locale-Parameter - Mock-Adapter gibt aktuell alle Locales gleich zurück (TODO: Locale-spezifische Mock-Daten) - Bei Contentful-Integration: Adapter würde `locale`-Parameter an Contentful API weitergeben **Beispiel GraphQL Query:** ```graphql query { page(slug: "/about", locale: "de") { headline # Deutsche Übersetzung subheadline } page(slug: "/about", locale: "en") { headline # Englische Übersetzung subheadline } } ``` ## 🔮 Roadmap - [ ] Query Complexity Schema-Realm-Problem dauerhaft lösen - [ ] Database Adapter (PostgreSQL, MySQL) - [ ] Headless CMS Adapter (Contentful, Strapi) - [ ] Locale-spezifische Inhalte implementieren - [ ] Fallback-Locales konfigurieren - [ ] Rate Limiting - [ ] Authentication/Authorization ✅ (implementiert) - [ ] GraphQL Subscriptions - [ ] i18n: Weitere Sprachen hinzufügen - [ ] i18n: Locale-spezifische Mock-Daten für CMS - [ ] E2E Tests ## 🤝 Beitragen 1. Fork das Repository 2. Erstelle einen Feature Branch (`git checkout -b feature/AmazingFeature`) 3. Committe deine Änderungen (`git commit -m 'Add some AmazingFeature'`) 4. Push zum Branch (`git push origin feature/AmazingFeature`) 5. Öffne einen Pull Request ## 📝 Lizenz Dieses Projekt ist privat. ## 👥 Autoren - Entwickelt mit ❤️ für moderne E-Commerce-Lösungen --- **Hinweis:** Dieses Projekt ist in aktiver Entwicklung. Für Fragen oder Probleme öffne bitte ein Issue.