Blog • 21. September 2025 • von Unknown Author

Die Architektur im Detail

Hero image

Next.js, Payload CMS und die Macht der Composability

Next.js, Payload CMS und die Macht der Composability

Teil 2 der Serie "Enterprise-Ready Multi-Tenant-Architekturen mit Next.js und Payload CMS"

Nachdem wir im ersten Artikel die geschäftlichen Vorteile moderner Architekturen beleuchtet haben, öffnen wir heute die Motorhaube. Wie erreicht man PageSpeed-Scores von 95+? Wie verwaltet man dutzende Mandanten mit einer Codebasis? Die Antwort liegt in der durchdachten Kombination modernster Technologien und bewährter Patterns.

Der Technology Stack: Best-of-Breed statt Kompromisse

Moderne Architekturen folgen dem Prinzip der Composability: Statt auf monolithische All-in-One-Lösungen zu setzen, komponieren wir die besten Tools für jeden Zweck zu einem harmonischen Ganzen.

typescript
// Unser Enterprise Stack im Überblick
const enterpriseStack = {
  frontend: {
    framework: "Next.js 15.1.6",
    ui: "React 19 mit Server Components",
    styling: "Tailwind CSS + CSS Modules",
    types: "TypeScript durchgängig"
  },
  backend: {
    cms: "Payload CMS 3.48",
    database: "PostgreSQL 16",
    storage: "Vercel Blob Storage",
    auth: "NextAuth + Payload Users"
  },
  infrastructure: {
    hosting: "Vercel Edge Network",
    cdn: "Global Edge Caching",
    monitoring: "Vercel Analytics",
    ci: "GitHub Actions"
  }
}
```
Jede Komponente wurde bewusst gewählt und spielt eine spezifische Rolle im Gesamtsystem.
React Server Components (RSC) sind der Game-Changer für Performance. Statt den gesamten React-Code an den Browser zu senden, wird er auf dem Server ausgeführt. Das Ergebnis: 70% kleinere JavaScript-Bundles.

Die Lösung mit Server Components

tsx
// Modern: Ausführung auf dem Server
export async function ProductList() {
  // Direkte Datenbankabfrage, kein API-Call nötig
  const products = await getProductsFromDB()
  
  // HTML wird fertig gerendert zum Client geschickt
  return (
    <div className="grid grid-cols-3 gap-4">
      {products.map(product => (
        <ProductCard key={product.id} {...product} />
      ))}
    </div>
  )
}
// Bundle-Größe: 0KB - nur HTML wird übertragen
```
Der Unterschied ist dramatisch: Keine Loading States, kein Layout Shift, sofortiger Content.
Payload CMS ist nicht "noch ein Headless CMS". Es ist von Grund auf für moderne Architekturen konzipiert, mit TypeScript als First-Class-Citizen.

// Automatisch generierte TypeScript-Typesexport interface Product { id: string title: string price: number tenant: string | Tenant createdAt: string updatedAt: string}```

Diese Types fließen durch die gesamte Anwendung – von der Datenbank bis zum Frontend. Keine Runtime-Fehler mehr durch Tippfehler oder falsche Annahmen.

Das Multi-Tenant Plugin

Das Herzstück unserer Architektur ist das offizielle Multi-Tenant-Plugin von Payload:

typescript
// Multi-Tenant Konfiguration
import { multiTenantPlugin } from '@payloadcms/plugin-multi-tenant'
export default buildConfig({
  plugins: [
    multiTenantPlugin({
      tenantCollection: 'tenants',
      tenantField: 'tenant',
      isolationStrategy: 'filter', // Automatische Filterung
      sharedCollections: ['media'], // Geteilte Ressourcen
    })
  ],
  // Rest der Konfiguration
})

Mit dieser Konfiguration wird jede Abfrage automatisch nach Mandant gefiltert. Keine Chance für Datenlecks zwischen Mandanten.

Die Service Layer: Clean Architecture in der Praxis

Zwischen Frontend und CMS liegt unsere Service Layer – das Gehirn der Anwendung:

typescript
// /lib/payload-services.ts
import { cache } from 'react'
import { getPayload } from 'payload'
// Request-Level Caching mit React.cache
export const getTenantBySlug = cache(async (slug: string) => {
  const payload = await getPayload()
  
  const result = await payload.find({
    collection: 'tenants',
    where: {
      slug: { equals: slug }
    },
    depth: 2 // Lade Beziehungen mit
  })
  
  return result.docs[0] || null
})
// Tenant-aware Datenabfrage
export const getProductsByTenant = cache(async (
  tenantId: string,
  options?: {
    limit?: number
    category?: string
    featured?: boolean
  }
) => {
  const payload = await getPayload()
  
  const where: Where = {
    tenant: { equals: tenantId },
    status: { equals: 'published' }
  }
  
  if (options?.category) {
    where.category = { equals: options.category }
  }
  
  if (options?.featured) {
    where.featured = { equals: true }
  }
  
  const result = await payload.find({
    collection: 'products',
    where,
    limit: options?.limit || 10,
    sort: '-createdAt'
  })
  
  return result.docs
})

Diese Service Layer bietet mehrere Vorteile:

  1. Zentrale Business Logic: Alle Geschäftsregeln an einem Ort
  2. Type Safety: Durchgängige TypeScript-Unterstützung
  3. Caching: Automatisches Request-Level-Caching
  4. Testbarkeit: Services können isoliert getestet werden

PostgreSQL: Die Basis für Datenintegrität

Während NoSQL-Datenbanken ihren Platz haben, bietet PostgreSQL für Multi-Tenant-Systeme unschlagbare Vorteile:

sql
-- Automatische Tenant-Isolation auf Datenbankebene
CREATE POLICY tenant_isolation ON products
  FOR ALL
  USING (tenant_id = current_setting('app.current_tenant')::uuid);
-- Referentielle Integrität
ALTER TABLE products
  ADD CONSTRAINT fk_tenant
  FOREIGN KEY (tenant_id)
  REFERENCES tenants(id)
  ON DELETE CASCADE;
-- Performante Indizes für Multi-Tenant-Abfragen
CREATE INDEX idx_products_tenant_status 
  ON products(tenant_id, status) 
  WHERE deleted_at IS NULL;

PostgreSQL garantiert ACID-Compliance, unterstützt komplexe Abfragen und skaliert vertikal bis zu enormen Datenmengen.

Das Routing-System: Subdomain-Magic

Wie erkennt das System automatisch den richtigen Mandanten? Durch intelligentes Routing:

typescript
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export async function middleware(request: NextRequest) {
  const hostname = request.headers.get('host') || ''
  
  // Extrahiere Subdomain
  const subdomain = hostname.split('.')[0]
  
  // Entwicklung: subdomain.localhost:3000
  // Produktion: subdomain.ihredomain.de
  
  if (subdomain && subdomain !== 'www' && subdomain !== 'admin') {
    // Rewrite zu tenant-spezifischer Route
    return NextResponse.rewrite(
      new URL(/${subdomain}${request.nextUrl.pathname}, request.url)
    )
  }
  
  return NextResponse.next()
}
export const config = {
  matcher: ['/((?!api|_next/static|favicon.ico).*)']
}

Dieses Pattern ermöglicht URLs wie:

  • berlin.ihrefirma.de → Berlin-Niederlassung
  • shop.ihrefirma.de → Online-Shop
  • partner.ihrefirma.de → Partner-Portal

Alles mit derselben Codebasis.

Component-Driven Development: Der Baukasten-Ansatz

Moderne Architekturen sind modular. Jede Komponente ist ein eigenständiger, wiederverwendbarer Baustein:

tsx
// Basis-Komponente mit Tenant-Theming
export const Button: React.FC<ButtonProps> = ({ 
  children, 
  variant = 'primary',
  tenantTheme 
}) => {
  const styles = {
    primary: {
      backgroundColor: tenantTheme?.primaryColor || '#0070f3',
      color: tenantTheme?.primaryTextColor || '#ffffff'
    },
    secondary: {
      borderColor: tenantTheme?.primaryColor || '#0070f3',
      color: tenantTheme?.primaryColor || '#0070f3'
    }
  }
  
  return (
    <button 
      className="px-6 py-3 rounded-lg transition-all"
      style={styles[variant]}
    >
      {children}
    </button>
  )
}
// Verwendung in verschiedenen Kontexten
<Button tenantTheme={berlinTheme}>Jetzt kaufen</Button>
<Button tenantTheme={münchenTheme} variant="secondary">Mehr erfahren</Button>

Einmal entwickelt, überall einsetzbar, individuell anpassbar.

Performance-Optimierung: Built-in, nicht Bolt-on

Performance ist kein nachträgliches Feature, sondern integraler Bestandteil der Architektur:

Parallele Datenabfragen

typescript
// Schlecht: Sequentielle Abfragen
const tenant = await getTenant(slug)
const pages = await getPages(tenant.id)  // Wartet auf tenant
const posts = await getPosts(tenant.id)  // Wartet auf pages
// Gesamtzeit: 300ms + 200ms + 250ms = 750ms
// Gut: Parallele Abfragen
const [tenant, settings] = await Promise.all([
  getTenant(slug),
  getGlobalSettings()
])
const [pages, posts, products] = await Promise.all([
  getPages(tenant.id),
  getPosts(tenant.id),
  getProducts(tenant.id)
])
// Gesamtzeit: max(300ms, 250ms) = 300ms

Edge Caching

typescript
// Statische Seiten mit ISR (Incremental Static Regeneration)
export const revalidate = 3600 // Cache für 1 Stunde
export async function generateStaticParams() {
  const tenants = await getAllTenants()
  const pages = await getAllPages()
  
  return tenants.flatMap(tenant =>
    pages.map(page => ({
      tenantSlug: tenant.slug,
      pageSlug: page.slug
    }))
  )
}
// Seiten werden beim ersten Besuch generiert und gecacht

Access Control: Sicherheit by Design

Multi-Tenant-Systeme müssen Daten strikt isolieren. Unser Access Control System garantiert dies:

typescript
// Standardisierte Access Control Patterns
export const publicTenantReadAccess: Access = ({ req }) => {
  // Öffentliche Leser sehen nur ihren Mandanten
  if (!req.user) {
    return {
      tenant: {
        equals: req.context?.tenant?.id
      }
    }
  }
  return true
}
export const tenantAdminAccess: Access = ({ req }) => {
  const user = req.user
  
  // Super-Admins haben vollen Zugriff
  if (user?.role === 'super-admin') return true
  
  // Tenant-Admins nur auf ihren Bereich
  if (user?.role === 'tenant-admin') {
    return {
      tenant: {
        equals: user.tenant?.id
      }
    }
  }
  
  return false
}

Diese Patterns werden konsequent auf alle Collections angewendet. Kein Datenleck möglich.

Die Entwickler-Experience: Produktivität durch Tooling

Eine gute Architektur macht Entwickler produktiv und glücklich:

bash
pnpm generate:types  # Generiert Types aus Payload-Schema
pnpm dev  # Next.js Fast Refresh + Payload Admin
pnpm lint  # ESLint + Prettier
pnpm migrate:create  # Erstellt neue Migration
pnpm migrate:run     # Führt Migrations aus
pnpm test           # Jest + React Testing Library
pnpm test:e2e       # Playwright E2E Tests

Die Werkzeuge arbeiten nahtlos zusammen. Keine Konfigurationshölle, keine Inkompatibilitäten.

Real-World Patterns: Aus der Praxis für die Praxis

Pattern 1: Lazy Loading für Heavy Components

tsx
// Dynamischer Import für große Komponenten
import dynamic from 'next/dynamic'
const InteractiveMap = dynamic(
  () => import('@/components/InteractiveMap'),
  { 
    loading: () => <MapSkeleton />,
    ssr: false  // Nur client-seitig laden
  }
)
// Komponente wird erst geladen, wenn benötigt
export function ContactPage() {
  return (
    <div>
      <h1>Kontakt</h1>
      <Suspense fallback={<MapSkeleton />}>
        <InteractiveMap />
      </Suspense>
    </div>
  )
}

Pattern 2: Optimistic Updates

tsx
// Sofortiges UI-Feedback, während Server arbeitet
export function LikeButton({ postId, initialLikes }) {
  const [likes, setLikes] = useState(initialLikes)
  const [isLiking, setIsLiking] = useState(false)
  
  async function handleLike() {
    // Optimistic Update
    setLikes(prev => prev + 1)
    setIsLiking(true)
    
    try {
      await likePost(postId)
    } catch (error) {
      // Rollback bei Fehler
      setLikes(prev => prev - 1)
      toast.error('Fehler beim Liken')
    } finally {
      setIsLiking(false)
    }
  }
  
  return (
    <button onClick={handleLike} disabled={isLiking}>
      ❤️ {likes}
    </button>
  )
}
```
Die vorgestellte Architektur ist kein zufälliges Zusammenspiel von Technologien. Jede Komponente wurde bewusst gewählt und optimiert, um zusammen ein System zu schaffen, das:
Diese Architektur ist nicht theoretisch – sie läuft produktiv, sie skaliert, sie liefert messbare Geschäftsergebnisse.
✅ React Server Components reduzieren Bundle-Größen um 70%
✅ Payload CMS bietet Enterprise-Features out-of-the-box
✅ Service Layer Pattern sorgt für Clean Architecture
✅ PostgreSQL garantiert Datenintegrität
✅ Component-Driven Development ermöglicht Wiederverwendung
Im nächsten Artikel: "Performance als Wettbewerbsvorteil" – Wir zeigen konkret, wie wir 95+ PageSpeed Scores erreichen und was das für Ihr Business bedeutet.
Bleiben Sie dran für Teil 3 der Serie →
Dies ist Teil 2 einer 5-teiligen Serie über Enterprise-Ready Multi-Tenant-Architekturen.

Die Architektur-Entscheidungen: Das "Warum" hinter dem "Was"

Warum Next.js statt Gatsby/Remix/Astro?

  • App Router: Modernste React-Features out-of-the-box
  • Vercel-Integration: Optimales Deployment und Edge Functions
  • Hybrid Rendering: SSG, SSR und ISR in einer App
  • Community: Größtes Ökosystem, beste Unterstützung

Warum Payload CMS statt Strapi/Directus/Sanity?

  • TypeScript-First: Durchgängige Type-Safety
  • Code-basierte Konfiguration: Versionierbar, reviewbar
  • Multi-Tenant-Plugin: Enterprise-Feature out-of-the-box
  • Local API: Keine HTTP-Overhead für Server-Abfragen

Warum PostgreSQL statt MongoDB/MySQL?

  • ACID-Compliance: Transaktionale Integrität
  • JSON-Support: Flexibilität wo nötig
  • Row-Level Security: Native Multi-Tenant-Unterstützung
  • Bewährt: 30+ Jahre Entwicklung und Optimierung

Fazit: Die Summe ist größer als ihre Teile

Die vorgestellte Architektur ist kein zufälliges Zusammenspiel von Technologien. Jede Komponente wurde bewusst gewählt und optimiert, um zusammen ein System zu schaffen, das:

  • Performant ist (95+ PageSpeed)
  • Skalierbar wächst (unbegrenzte Mandanten)
  • Wartbar bleibt (klare Patterns)
  • Sicher operiert (strikte Isolation)
  • Entwicklerfreundlich funktioniert (moderne Tools)

Diese Architektur ist nicht theoretisch – sie läuft produktiv, sie skaliert, sie liefert messbare Geschäftsergebnisse.

Key Technical Takeaways

React Server Components reduzieren Bundle-Größen um 70%

  • Weniger JavaScript = schnellere Ladezeiten

Payload CMS bietet Enterprise-Features out-of-the-box

  • Multi-Tenant, Versionierung, Lokalisierung

Service Layer Pattern sorgt für Clean Architecture

  • Testbar, wartbar, erweiterbar

PostgreSQL garantiert Datenintegrität

  • ACID, Constraints, Row-Level Security

Component-Driven Development ermöglicht Wiederverwendung

  • Einmal bauen, überall nutzen

Im nächsten Artikel: "Performance als Wettbewerbsvorteil" – Wir zeigen konkret, wie wir 95+ PageSpeed Scores erreichen und was das für Ihr Business bedeutet.

Bleiben Sie dran für Teil 3 der Serie →

Über diese Serie

Dies ist Teil 2 einer 5-teiligen Serie über Enterprise-Ready Multi-Tenant-Architekturen.

Artikel Details

Autor
Unknown Author
Veröffentlicht
21. September 2025
Lesezeit
ca. 5 Min.

Teilen