Files
sell/README.md

11 KiB

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

# Dependencies installieren
npm install

# (Optional) Redis installieren (macOS)
brew install redis
brew services start redis

🏃 Quick Start

# 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:

# 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

# 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

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:
// middlelayer/adapters/myAdapter.ts
import type { DataAdapter } from './interface.js';

export class MyAdapter implements DataAdapter {
  async getProducts(limit?: number): Promise<Product[]> {
    // Implementierung
  }
  // ... weitere Methoden
}
  1. In middlelayer/adapters/config.ts registrieren:
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

npm run build

Docker (Beispiel)

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
RUN npm run build
CMD ["npm", "start"]

🧪 Testing

# 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

🐛 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:

import { useI18n } from "../lib/i18n/useI18n.js";

function MyComponent() {
  const { t } = useI18n("auth");
  return <h1>{t("login.title")}</h1>;
}

Verwendung in Alpine.js:

<div x-data="{ t: window.i18n?.t || ((key) => key) }">
  <button x-text="t('nav.login')"></button>
</div>

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:

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.