Amine Benamrouche · Projekte
← Zurück zu den Projekten

Klinica: mandantenfähiges Open-Source-SaaS für medizinische Kliniken

Überblick

Klinica ist ein mandantenfähiges SaaS für medizinische Kliniken: eine Subdomain pro Klinik, eine anpassbare öffentliche Landingpage, Online-Buchung und ein vollständiges Admin-Panel. Abrechnung, Testzeiträume und Tarifbeschränkungen laufen durchgängig über Paddle.

Live: klinica.space · Quellcode: github.com/AmineZCS/klinica

Ich habe es als eigenständiges Projekt gebaut, um eine vollständige mandantenfähige SaaS-Architektur von Grund auf und durchgängig zusammenzusetzen. Es ist auf einer AWS-Lightsail-VM deployt und in Betrieb. Cloudflare übernimmt DNS und SSL für die Hauptdomain und jede Mandanten-Subdomain, E-Mail-Erinnerungen laufen über echtes SMTP, und die Paddle-Abrechnung läuft im Sandbox-Modus.

Es ist bewusst eine kleinere Version der mandantenfähigen Gesundheitsplattform, an der ich beruflich bei HealthHub gearbeitet habe, und überträgt dieselben Kernkonzepte (Mandantenisolation, Abonnements, Terminplanung) in meine eigene Architektur.

Architektur

Klinica-Infrastruktur: Cloudflare und Caddy vor der Laravel-App, Routing pro Subdomain zu isolierten Mandantendatenbanken, synchrone und ausgehende Paddle-Aufrufe, lokaler Medienspeicher pro Mandant, ein geplanter Job, der für Buchungserinnerungen die zentrale und die Mandantendatenbanken liest, und eine Redis-gestützte Queue, die einen Worker für Mandantenbereitstellung und E-Mail versorgt

Anfragen laufen über Cloudflare und Caddy zu einem einzelnen Laravel-App-Prozess. Von dort entscheidet die Subdomain, wohin die Anfrage geht: Der Verkehr jeder Klinik wird zu ihrer eigenen isolierten Mandantendatenbank geroutet, während das Super-Admin-Panel und die eingehenden Paddle-Webhooks beide in die zentrale Datenbank schreiben. Die App ruft Paddle außerdem direkt für Checkout, Tarifwechsel und Kündigungen auf. Der Webhook-Pfad ist synchron und nicht über eine Queue gelöst, aber HMAC-verifiziert und idempotent. Branding-Assets der Mandanten (Logos, Bilder) liegen auf einem lokalen Datenträger-Volume, getrennt pro Mandant.

Ein geplanter Job führt den Ablauf der Buchungserinnerungen direkt aus: Er liest berechtigte Mandanten aus der zentralen Datenbank, wechselt dann in die jeweils eigene Datenbank jedes Mandanten, um anstehende Buchungen zu laden, und stellt die Erinnerungs-E-Mails in die Queue. Das geschieht sequenziell, ein Mandant nach dem anderen. Bei der aktuellen Größenordnung ist das in Ordnung, für eine deutlich größere Mandantenzahl ist es aber nicht in Blöcke aufgeteilt. Die Queue selbst (Redis und ein Worker) übernimmt nur zwei Aufgaben: die Bereitstellung der Datenbank eines neuen Mandanten bei der Anmeldung und den tatsächlichen Versand der eingereihten E-Mails.

Funktionen

  • Multi-Tenancy: Subdomain pro Mandant mit isolierten Datenbanken, umgesetzt mit stancl/tenancy. Die Anmeldung stellt einen neuen Mandanten asynchron bereit: Die Anfrage kehrt sofort zurück, die Datenbank wird in einem Queue-Job angelegt.
  • Abrechnung und Abonnements: Paddle-Integration mit Testzeiträumen, verlängerbaren Abonnements, Tarifwechseln, Kündigungen und Webhook-gesteuerter Zustandssynchronisation.
  • Tarifbeschränkungen: Limits für Starter und Pro (Patienten, Mitarbeitende, monatliche Buchungen, Speicher, E-Mail-Erinnerungen), durchgesetzt per Middleware.
  • Öffentliche Buchung: Jede Klinik erhält eine gebrandete Landingpage und einen Self-Service-Ablauf zur Terminbuchung.
  • Anpassbare Landingpages: Mandanten bearbeiten Branding, Farben, Kontaktdaten und Texte in ihrem Einstellungsbereich, mit Live-Vorschau vor dem Speichern.
  • Zwei Filament-Panels: Ein zentrales Super-Admin-Panel (Mandanten, Abonnements, Analytik) und ein Klinik-Panel pro Mandant (Buchungen, Patienten, Leistungen, Personal, Kalender).
  • Rollen und Berechtigungen: RBAC pro Mandant mit spatie/laravel-permission.
  • Mehrsprachig: Englisch, Französisch, Deutsch, Spanisch.
  • SEO: Sitemaps pro Mandant, llms.txt, IndexNow-Übermittlung und noindex für Mandanten, die noch nicht öffentlich sein sollen.
  • Audit-Log: Nachverfolgung der Mandantenaktivität mit spatie/laravel-activitylog.
  • DSGVO: Datenexport pro Mandant.

Produktion & Deployment

Klinica läuft hinter FrankenPHP + Laravel Octane auf einer einzelnen AWS-Lightsail-VM. Der gesamte Stack (App, Queue-Worker, Scheduler, MySQL, Redis und Caddy als Edge-Proxy) ist in einer Docker-Compose-Datei für die Produktion definiert. Cloudflare übernimmt DNS und SSL für die Hauptdomain und jede Mandanten-Subdomain, wobei Caddy vor der App das TLS terminiert.

Deployments laufen über eine GitHub-Actions-Pipeline: Lint und Tests, Image bauen, Migrationen ausführen (einschließlich Migrationen pro Mandant), umschalten und anschließend Smoke-Tests durchführen, mit automatischem Rollback auf das vorherige Image, falls etwas fehlschlägt. Die Testsuite (Pest 4) deckt die kritischen Pfade ab: Abrechnung, Webhook-Signaturprüfung und Idempotenz sowie Mandantenbereitstellung. Die vollständige Deploy-Pipeline und die Edge-Konfiguration liegen im Repository.

Technischer Stack

KomponenteTechnologien
BackendPHP 8.5, Laravel 13
Admin- / Mandanten-PanelsFilament 5
FrontendLivewire 4, Alpine.js, Tailwind CSS 4
DatenbankMySQL 8, Redis
Multi-Tenancystancl/tenancy v3 (Datenbank pro Mandant)
AbrechnungPaddle
ProduktionslaufzeitFrankenPHP, Laravel Octane 2
Edge / Reverse ProxyCaddy
Lokale EntwicklungLaravel Sail
TestsPest 4
DeploymentDocker, GitHub Actions
InfrastrukturAWS Lightsail, Cloudflare (DNS/SSL)