Zum Inhalt springen

Projekte

Raum-Buchungssystem — Symfony 7 + Nuxt 4

2026

Entkoppelte REST-API mit SPA-Frontend: race-condition-freie Konfliktprüfung, Genehmigungs-Workflows, iCalendar-Export und 42 PHPUnit-Tests.

  • Symfony 7
  • Doctrine
  • JWT
  • Nuxt 4
  • TypeScript
  • Docker
  • PHPUnit

Die Aufgabe

Ein Meeting-Raum-Buchungssystem als Arbeitsprobe: Räume verwalten, Verfügbarkeiten prüfen, Buchungen anlegen und verschieben — mit Admin-Genehmigungen und sauberer API. Bewusst als entkoppeltes System gebaut: Symfony-7-REST-API und Nuxt-4-SPA, verbunden nur über HTTP.

Das interessanteste Problem: Doppelbuchungen

Zwei Nutzer buchen denselben Raum zur selben Zeit — klassische Race Condition. Die Lösung: eine Transaktion mit Overlap-Query vor dem INSERT.

SELECT * FROM booking
WHERE room_id = :room
  AND cancelled_at IS NULL
  AND start_at < :newEnd
  AND end_at > :newStart

SQLite serialisiert Schreibvorgänge (ein Schreiber zur Zeit im WAL-Modus), damit ist der Check-then-Insert innerhalb der Transaktion race-condition-frei. Der Trade-off ist dokumentiert: Bei einem Wechsel auf PostgreSQL/MySQL braucht es SELECT FOR UPDATE auf Raumebene.

Weitere Entscheidungen

  • Stateless JWT-Auth (RSA-256) — horizontal skalierbar, ohne Session-Storage; die Trade-offs (Token-Revocation, Cookie-Scope) sind im Entscheidungslog festgehalten.
  • Soft Delete für Stornierungencancelled_at statt DELETE erhält die Historie für Auswertungen.
  • Genehmigungs-Workflows — Buchungen von Basisnutzern starten als pending; Admins genehmigen Buchungen und Raum-Änderungsanträge.
  • iCalendar-Export (RFC 5545) — jede Buchung als .ics direkt in den Kalender.
  • Raumübergreifende Verfügbarkeitssuche — „welcher Raum ist am Dienstag 14–16 Uhr für 8 Personen frei?” als ein API-Call.

Qualität & Übergabe

42 PHPUnit-Tests (Repository, Controller, Services), Docker-Compose-Setup mit einem Befehl, und eine docs/ mit Mermaid-Architekturdiagrammen, API-Referenz und Anforderungsabgleich. So sieht bei mir Übergabe aus — auch bei kleinen Projekten.