Kapitel 1

Systemarchitektur

Die Plattform besteht aus den Shop-Anwendungen, den Self-Service-Diensten und gemeinsam genutzter Infrastruktur — alles hinter einem einzigen Reverse Proxy. Dieses Kapitel zeigt, was wo läuft, wie die Teile miteinander sprechen, wie ein neuer Shop entsteht und wo die Grenzen zwischen den Mandanten verlaufen.

Systemübersicht

Aller Verkehr betritt die Plattform über Traefik. Der Reverse Proxy terminiert TLS, erzwingt HTTPS und verteilt die Anfragen anhand des Hostnamens auf die Container dahinter. Kein Anwendungsdienst ist direkt aus dem Internet erreichbar.

Internet · Port 80 / 443TraefikReverse Proxy · TLS via Let’s EncryptCockpitSvelteKit · Docker · :3000cockpit.wein-online-verkaufen.deMedusaCommerce-Backend · :9000medusa.wein-online-verkaufen.deStorefronts × NEin Container je Weingut · :3000<slug>.wein-online-verkaufen.deAdmin APIStore APIPostgreSQLpgvector:pg17 · :5432nur Docker-internRedisEvent-Bus · Sessions127.0.0.1 · :6379MinIO / S3Bucket „medusa“s3.wein-online-verkaufen.deStripe (Connect)Durchgezogen: interner Verkehr · Gestrichelt: externer Dienst
Systemübersicht: Der gesamte Verkehr betritt die Plattform über Traefik. Cockpit und Storefronts sprechen ausschließlich mit Medusa, nie direkt mit der Datenbank. Nicht eingezeichnet sind Landingpage, Self-Service-API und Master Dashboard — sie hängen ebenfalls hinter Traefik und sind weiter unten beschrieben.

Die drei Anwendungen darunter haben klar getrennte Aufgaben: Das Cockpit ist die Oberfläche für Winzer, die Storefronts sind die Shops für die Kundschaft, und Medusa ist die einzige Instanz, die tatsächlich mit der Datenbank spricht. Weder Cockpit noch Storefront haben eine Datenbankverbindung — sie kennen nur HTTP-Endpunkte.

Daneben stehen die Self-Service-Dienste: die Landingpage mit der Registrierung, die Self-Service-API, die Accounts, Abos und die Einrichtung neuer Shops verwaltet, und das Master Dashboard für den Betreiber. Beide Dienste halten ihre eigenen Daten in einer kleinen SQLite-Datenbank; die Shop-Daten selbst bleiben in Medusa.

Komponenten im Überblick

Alle Dienste laufen als Docker-Container auf einem einzelnen Server. Die Ports in der Tabelle sind Container-Ports; nach außen sind nur 80 und 443 offen.

Laufende Dienste
KomponenteBetriebsartPortTechnologieStatus
TraefikDocker80 / 443traefik:latestLive
MedusaDocker9000Medusa v2.13.1Live
CockpitDocker3000 (intern)SvelteKit · adapter-nodeLive
StorefrontsDocker × N3000 (intern)SvelteKit · adapter-nodeLive
PostgreSQLDocker5432 (nur intern)pgvector/pgvector:pg17Live
RedisDocker6379 (127.0.0.1)redis:alpineLive
MinIODocker9000 / 9001minio:latestLive
Self-Service-APIDocker3000 (intern)SvelteKit · SQLiteLive
Master DashboardDocker3000 (intern)SvelteKit · SQLiteLive
LandingpageDocker3000 (intern)SvelteKit · adapter-nodeLive
RegistryDocker5000registryLive
AnalyticsDocker3000 (intern)UmamiLive

Domains und Routing

Stammdomain ist wein-online-verkaufen.de. Jeder Dienst bekommt eine eigene Subdomain, jedes Weingut eine Shop-Subdomain nach dem Muster <slug>.wein-online-verkaufen.de — oder, wenn es eine verbunden hat, die eigene Domain.

Traefik-Routing
DomainZielZweck
wein-online-verkaufen.destorefront-landing:3000Landingpage mit Registrierung (/register) und dieser Dokumentation
cockpit.wein-online-verkaufen.decockpit:3000Cockpit der Weingüter — vom kostenlosen Account bis zum laufenden Shop
admin.wein-online-verkaufen.deweinshop-admin:3000Master Dashboard des Betreibers
self-service.wein-online-verkaufen.deself-service:3000Self-Service-API: Accounts, Abo, Provisioning
medusa.wein-online-verkaufen.demedusa:9000Commerce-API (Store + Admin), intern genutzt
<slug>.wein-online-verkaufen.destorefront-<slug>:3000Shop eines Weinguts
s3.wein-online-verkaufen.deminio:9000Medien-Auslieferung (S3-API)

Zwei Wege führen zu einer Route. Cockpit, Medusa und Self-Service-API melden sich über Docker-Labels an. Landingpage, Master Dashboard und alle Shops haben eine Dateiroute im Verzeichnis traefik/dynamic, das Traefik laufend beobachtet — für neue Shops schreibt die Provisioning-Pipeline diese Datei selbst. In keinem Fall muss der Proxy neu geladen werden.

Für *.wein-online-verkaufen.de gibt es einen Wildcard-DNS-Eintrag auf den Server. Eine neue Shop-Subdomain braucht deshalb keinen DNS-Schritt. Das Zertifikat holt Traefik beim ersten Aufruf über die TLS-Challenge — eines pro Hostname, kein Wildcard-Zertifikat — und erneuert es danach automatisch.

Self-Service und Provisioning

Neue Weingüter kommen ohne Zutun des Betreibers auf die Plattform. Drei Dienste teilen sich die Arbeit:

  • Landingpage — nimmt unter /register die E-Mail-Adresse entgegen
  • Self-Service-API — legt den Account an, verschickt den Anmeldelink, startet den Stripe Checkout fürs Abo, empfängt den Stripe-Webhook und führt die Provisioning-Pipeline aus
  • Cockpit — führt das Weingut durch Stammdaten, Tarifwahl und Zahlung und zeigt den Fortschritt der Einrichtung

Die Provisioning-Pipeline startet erst nach bestätigter Zahlung. Sie legt in Medusa Schlüssel, Benutzer, Sales Channel sowie Lager und Versand an, schreibt die Cockpit-Stammdaten, erzeugt Medien-Bucket und Stripe-Connect-Konto, startet den Storefront-Container aus dem Template-Image, schreibt die Traefik-Route und schickt am Ende die Go-live-E-Mail. Jeder Schritt wird geprüft; scheitert einer, wird zurückgerollt. Die vollständige Liste steht im Onboarding-Kapitel.

Status-Modell

Jedes Weingut trägt in der Self-Service-API einen Status. Der reguläre Weg führt von registered über onboarding zu active.

Status eines Weinguts
StatusAusgelöst durchBedeutung
registeredRegistrierung mit E-Mail-AdresseAnmeldelink verschickt, noch keine Stammdaten
onboardingStammdaten und Passwort gespeichertKostenloser Account; nach der Zahlung läuft hier die Einrichtung
activeProvisioning-Pipeline abgeschlossenShop live, volles Cockpit
suspendedStripe meldet offene ZahlungVermerk für den Betreiber, sperrt nichts automatisch
cancelledAbo bei Stripe gekündigtVermerk für den Betreiber, sperrt nichts automatisch

Unabhängig vom Status gibt es die Sperre („freeze“), die der Betreiber im Master Dashboard setzt und wieder aufhebt. Ein gesperrter Shop leitet auf eine Hinweisseite um, nur Impressum und Datenschutz bleiben erreichbar; das Cockpit des Weinguts zeigt ebenfalls nur noch den Hinweis. Ist die Self-Service-API nicht erreichbar, bleiben Shops offen — die Sperre ist bewusst fail-open.

Master Dashboard

Unter admin.wein-online-verkaufen.de sieht der Betreiber alle Weingüter: Interessenten und frisch Registrierte unter „Prospects“, alle Weingüter mit Stammdaten unter „Kunden“ — mit Tarif, Status und Stand der Einrichtung. Dazu kommen das Feedback aus den Cockpits und die Plattform-Konfiguration. Von hier aus werden Shops gesperrt und reaktiviert.

Docker-Netzwerke

Zwei Netzwerke trennen das, was von außen erreichbar sein muss, von dem, was es nicht sein darf. PostgreSQL und Redis liegen ausschließlich im internen Netz und haben keinen veröffentlichten Port.

web externes Bridge-Netzwerk

Alles, was von außen erreichbar sein muss.

  • Traefik :80/:443
  • Cockpit :3000
  • Medusa :9000
  • Storefronts :3000 × N
  • MinIO :9000/:9001
  • Registry :5000
  • Analytics
root_default internes Compose-Netzwerk

Kein Port nach außen. Erreichbar nur aus dem Netz heraus.

  • PostgreSQL :5432
  • Redis :6379

Traefik ist der einzige Container, der beide Welten berührt: Er hängt im Netz web und ist als einziger von außen ansprechbar.

Datenfluss

Vier Wege beschreiben praktisch alles, was in der Plattform passiert.

Kundin besucht einen Shop

Serverseitig gerendert — die erste Antwort enthält bereits das fertige HTML.

  1. Browser
  2. Traefik (TLS)
  3. Storefront-Container
  4. Medusa Store API
  5. PostgreSQL
  6. MinIO (Bilder)
  7. HTML an den Browser

Winzer arbeitet im Cockpit

Nach dem Login läuft die Oberfläche als Single-Page-Anwendung weiter.

  1. Browser
  2. Traefik (TLS)
  3. Cockpit-Container
  4. Medusa Admin API (JWT)
  5. PostgreSQL
  6. MinIO (Upload)

Weingut bucht einen Tarif

Erst die bestätigte Zahlung startet die Einrichtung — Webhook oder Rückkehr aus Stripe, je nachdem, was zuerst ankommt.

  1. Cockpit /shop-einrichten
  2. Self-Service-API
  3. Stripe Checkout
  4. Webhook an Self-Service-API
  5. Provisioning-Pipeline
  6. Shop unter <slug>.wein-online-verkaufen.de

Kundin bezahlt

Der Payment Intent entsteht im Stripe-Konto des Weinguts, nicht im Plattform-Konto.

  1. Checkout im Shop
  2. Medusa Payment-Modul
  3. Stripe Connect (Weingut)
  4. Webhook zurück an Medusa
  5. Bestellung + E-Mail

Mandantentrennung

Alle Weingüter teilen sich eine Datenbank. Getrennt werden sie über den Sales Channel von Medusa — ein Konzept, das ohnehin für Vertriebskanäle vorgesehen ist und hier als Mandantengrenze dient.

Auf der Kundenseite

Jede Storefront kennt genau eine sales_channel_id und einen Publishable API Key, der auf diesen Channel eingeschränkt ist. Die Store API liefert damit ausschließlich Produkte dieses Kanals aus. Selbst wenn jemand die Produkt-ID eines fremden Weinguts errät, kommt mit dem falschen Key nichts zurück.

Auf der Winzerseite

Der Cockpit-Benutzer trägt seine sales_channel_id in den Metadaten. Jeder Lese- und Schreibzugriff über die Admin-Routen wird auf diesen Wert eingeschränkt — Weine, Bestellungen, Beiträge, Lagen, Rebsorten und das Winzer-Profil.

Was pro Mandant konfiguriert wird

  • TENANT_BASE_URL — die Medusa-Instanz, für alle gleich
  • TENANT_PUBLISHABLE_KEY — Store-API-Key des Mandanten
  • TENANT_SALES_CHANNEL_ID — die Mandantengrenze
  • TENANT_REGION_ID — Währung, Steuersatz, Lieferländer
  • TENANT_SHOP_DOMAIN und TENANT_SHOP_NAME — Domain und Anzeigename
  • TENANT_STRIPE_ACCOUNT_ID — das Stripe-Connect-Konto des Weinguts

Sicherheit

Der ehrliche Stand — inklusive dem, was noch offen ist.

Sicherheitsmaßnahmen
BereichStandBeschreibung
TLS / HTTPSErledigtLet’s Encrypt über Traefik, automatische Erneuerung
PostgreSQL-PortErledigtNicht mehr auf dem Host geöffnet, nur Docker-intern
RedisErledigtPasswortgeschützt, gebunden an 127.0.0.1
Medusa AdminErledigtJWT-Authentifizierung, Scope über Sales Channel
MinIOErledigtAccess Key und Secret Key
BackupsErledigtNächtlicher pg_dump per Cron, externe Ablage
SecretsOffenUmgebungsvariablen liegen im Klartext auf dem Host
Host-FirewallOffenKeine ufw-/iptables-Regeln aktiv

Skalierung

Heute läuft alles auf einem Server. Das ist eine bewusste Entscheidung: Bei einer einstelligen Zahl von Weingütern kostet verteilte Infrastruktur mehr Aufmerksamkeit, als sie einbringt.

Vertikal — der aktuelle Weg

Mehr RAM und CPU für den Host, dazu PostgreSQL-Tuning. Trägt komfortabel bis in den Bereich von etwa 20 bis 30 Mandanten.

Horizontal — der vorbereitete Weg

  • PostgreSQL auf einen eigenen Server oder einen Managed Service
  • Redis auf eine eigene Instanz
  • Medusa mehrfach hinter einem Load Balancer
  • Storefronts beliebig verteilen — sie sind zustandslos und nur über Umgebungsvariablen konfiguriert
  • MinIO gegen einen S3-Anbieter tauschen; die Schnittstelle bleibt dieselbe

Portabilität

Ein Serverumzug besteht aus fünf Schritten: Datenbank per pg_dump sichern und einspielen, Images aus der Registry ziehen, .env übernehmen, Compose-Stack starten, DNS umstellen. Es gibt keine Bindung an einen Hoster.