Table of Contents
Architecture
Dual-ID System
Each resource has two IDs with clearly distinct responsibilities:
| ID Type | Column | Format | Usage |
|---|---|---|---|
| Internal ID | id |
|BIGINT AUTO_INCREMENT
| Foreign key, joins within the database |
| External ID |public_id
| UUIDv7 | All API responses, URL parameters, sync log |
The external API and the sync layer only recognizepublic_id
. Internal primary keys are never exposed externally.
TheHasPublicId
trait (app/Models/Concerns/HasPublicId.php
) automatically generates the UUIDv7 in thecreating
hook viaStr::uuid7()
.
Multi-Tenancy
Organizations are the tenant anchor. EveryUser
belongs to exactly oneOrganization
, as do everyEvent
and everyRegistration
.
The URL parameter{organizationSlug}
activates theResolveOrganizationContext
middleware, which embeds the tenant context into the request lifecycle. All organization-scoped controllers check whether the requested resource belongs to the authenticated user’s organization (BOLA protection).
Branding fallback: If no event logo is available, the organization logo is used.
JWT Authentication
Login
└─ TokenService.issueTokenSet()
├─ Fingerprint prüfen → bekanntes Device wiederverwenden oder neues anlegen
├─ Access-Token ausgeben (HS256, 15 Min, Claims: sub, uid, dti, roles, org_id)
└─ Refresh-Token ausgeben (opak, SHA-256 gehashed, 30 Tage)
Jede authentifizierte Anfrage
└─ AuthenticateWithJwt-Middleware
├─ JWT dekodieren und iss/aud/token_use prüfen
└─ User aus DB laden und im Request-Resolver hinterlegen
Token-Rotation
└─ POST /api/v1/auth/refresh
├─ Alten Refresh-Token konsumieren und invalidieren
└─ Neues Token-Paar ausgeben (Replay-Schutz: Wiederverwendung widerruft Device)
Refresh tokens are transmitted as HttpOnly cookies. Access tokens (in-memory, 15 min) are never persisted in localStorage.
Eloquent Models (34)
Core: Event, Registration, RegistrationField, RegistrationFieldOption, User, Organization
Payment: Payment, PaymentAttempt, PaymentRefund, PaymentReader, OrganizationPaymentConfig, AddonOrder, AddonOrderItem, EventAddon
Compliance & Privacy: ConsentRecord, DataSubjectRequest, LegalHold, AuditEntry
Operational: CheckinRecord, Device, RefreshToken, ApiCredential, ExternalEventMapping
Messaging: NotificationTemplate, NotificationPreference, NotificationMessage, DeliveryAttempt
Search & Sync: SearchDocument, SyncCheckpoint, SyncMutation
Answers: RegistrationAnswer, RegistrationAnswerOptionSelection
Migrations (43)
Key schema patterns:
- Public IDs (UUIDs) on all primary entities
- Composite indexes for performance
- Soft deletes on: Events, Registrations, Payments
- Notification system: Template versioning (Channel + Version), idempotency keys, delivery attempt tracking
- Compliance-first: Consent records, DSR tracking, legal holds with metadata JSON
- Payment infrastructure: Multi-stage attempts/refunds, reader mapping, SumUp webhook support
- Add-on system: Stock tracking, soft deletes, sort order
- Audit trail: Device/actor tracking, user agent in AuditEntry
- Sync system: Checkpoints, mutations with device and idempotency tracking
Frontend: Vue 3 SPA
The frontend is a standalone Vue 3 single-page application atresources/js/spa/
.
Components (10)
Basic UI: AppBadge, AppButton, AppErrorBoundary, AppFormInput, AppFormSelect, AppNotice, AppPanel
Feature-specific: CheckinSearch, CheckinResult, QrScanner (check-in module)
Views (14)
| View | Description |
|---|---|
| LoginView | Login |
| SessionView | Session management |
| DashboardView | Dashboard |
| EventManagementView | Event Management |
| AttendeeManagementView | Attendee Management |
| PublicRegistrationView | Public Registration |
| PublicEventsOverviewView | Public Event Overview |
| MobileCheckinView | Mobile Check-in with QR Scanner |
| AuditLogView | Audit Log |
| ComplianceView | GDPR Management |
| SelfServiceView | Self-Service Portal |
| PretixCompatView | Pretix Compatibility |
| SearchView | Full-Text Search |
| SyncCenterView | Sync Center |
Pinia Stores
-auth.js
— JWT Handling, Login/Logout, Token Refresh, Device Management
-sync.js
— Offline sync status and progress indicator
Axios Client
Central client with request interceptor (authorization header) and response interceptor for automatic token refresh on HTTP 401. Concurrent 401 errors are queued and processed after a successful refresh.
Offline Synchronization
1. Bootstrap POST /api/v1/sync/bootstrap
→ Vollständiger Datensatz (Registrierungen, Felder, Antworten)
→ Checkpoint mit Snapshot-Cursor anlegen
→ Chunking bei großen Datensätzen
2. Offline Gerät arbeitet lokal, speichert Mutations (z.B. Check-ins)
3. Upload POST /api/v1/sync/upload
→ Lokale Mutations an Server senden
→ ConflictResolutionService prüft Konflikte
4. Delta POST /api/v1/sync/delta
→ Nur Änderungen seit letztem Checkpoint
→ Chunking für große Datensätze
Conflict resolution: Last-Write-Wins based on timestamps; rejected updates are placed insync_mutations
with the statusrejected
for manual review.
Payment Flow (SumUp)
Online-Registrierung
└─ POST /api/v1/public/payments/registrations/{id}/online-checkout
├─ PaymentCheckoutService erstellt Checkout bei SumUp
└─ Redirect-URL zurückgeben → Teilnehmender schließt Zahlung ab
Webhook → POST /api/v1/payments/webhooks/sumup
└─ WebhookProcessingService verifiziert HMAC-Signatur
└─ Payment-Status aktualisieren, Registration auf paid setzen
Terminal (Check-in)
└─ POST .../payment/terminal
├─ SumUpGateway initiiert Terminal-Checkout am gekoppelten Reader
└─ Polling oder Webhook signalisiert Abschluss
Fallback
└─ POST .../payment/online-fallback
→ Online-Checkout für Registrierungen mit Terminal-Ausfall
Service Layer
The business logic is divided into 27 domain-specific services (app/Services/
), grouped by domain: Auth, Audit, Branding, Compliance, Dashboard, Events, Notifications, Payments, Registration, Search, Security, Sync, Addons, Pretix.
Three shared controller traits (Concerns/
) reduce boilerplate:ResolvesActor
,ResolvesManagedEvent
,BuildsAuditContext
.
Queue Jobs
5 asynchronous jobs for search index maintenance (IndexEventJob
,IndexRegistrationJob
,ReindexOrganizationJob
,RemoveSearchDocumentJob
) and notification delivery (SendNotificationJob
), triggered via Eloquent observers and the NotificationService.
CI/CD
laravel.yml (Push/PR to master/main/develop)
- PHP 8.4, Node.js 24, SQLite in-memory
- Steps: Composer/npm install → Frontend Build → Key Gen → Migrate → Pint Lint → PHPStan → Pest Tests
- Security job: Composer and npm audits
docker-smoke.yml (master only)
- Build Docker image and start container
- Validate health endpoint
- Check migration status
Docker (Production)
3-service architecture:
| Service | Description |
|---|---|
| web | Nginx + PHP-FPM (Multi-Stage Build: PHP 8.4 + Node 24) |
| queue | Queue Worker |
| scheduler | Task Scheduler |
Infrastructure:
| Service | Configuration |
|---|---|
| MariaDB 11 | Production database |
| Redis 7 | Cache, Sessions, Queue (LRU Eviction, 128 MB) |
Health Checks: HTTP/up
for Web, Queue Monitor for Worker,redis-cli ping
for Redis.
Tests (29 files)
- 27 Feature Tests: Addon, Attendee, Audit Log, Auth, Branding, Compliance, Dashboard, Event Management, Payment, Registration, Search, SelfService, SyncCenter, Pretix Compat, Security
- 2 Unit Tests
- 18 Factories for test data generation
- Test isolation: SQLite
:memory:
, Array Cache/Session
Directory structure
RegSys/
├── app/
│ ├── Enums/ # PHP-Enums (UserRole, etc.)
│ ├── Http/
│ │ ├── Controllers/Api/V1/ # 20 REST-Controller + Auth/ + Concerns/
│ │ ├── Controllers/Api/Pretix/V1/ # Pretix-Kompatibilitäts-API
│ │ ├── Middleware/ # JWT, Pretix-Auth, Org-Context, Security-Headers
│ │ └── Requests/Api/V1/ # FormRequests mit Validierungsregeln
│ ├── Jobs/ # 5 Queue-Jobs (Suche, Benachrichtigungen)
│ ├── Models/ # 34 Eloquent-Models + Concerns/HasPublicId
│ ├── Observers/ # Event- und Registration-Lifecycle
│ └── Services/ # 27 Services in 14 Domänen-Namespaces
├── database/
│ ├── factories/ # 18 Model-Factories für Tests
│ └── migrations/ # 43 Migrationen
├── docker/entrypoint.sh # Container-Startskript
├── resources/js/spa/ # Vue 3 SPA
│ ├── components/ # 10 Komponenten (UI + Check-in)
│ ├── views/ # 14 Vue-Views
│ ├── stores/ # Pinia: auth.js, sync.js
│ ├── router/index.js # Lazy-Loading + Route Guards
│ └── utils/ # Axios-Client, Formatters
├── routes/api.php # Alle API-Routen
├── tests/ # 29 Test-Dateien (Feature + Unit)
├── Dockerfile # Multi-Stage Build (PHP 8.4 + Node 24)
├── docker-compose.yml # Dev-Setup (SQLite, Port 18080)
└── docker-compose.production.yml # Prod-Setup (MariaDB 11, Redis 7)