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)