Campervan Rental Booking Engine
Multi-region campervan rental platform pairing a Symfony API with master-slave MySQL and an async payment queue for idempotent, retry-safe checkout at scale.
- Client
- Confidential · Mobility / Rental
- Role
- {{TODO: e.g. Solution Architect / Senior Backend Engineer}}
- Period
- {{TODO: e.g. 2022–2024}}
Summary
Overview
The platform is the backend API behind a campervan rental booking engine, serving a customer booking app and an internal backoffice across EU and SG regions. It manages availability, fleet, rental contracts, invoicing and payments through a single Symfony core, with real-time inventory fed by an external allotment scheduler.
The central architectural challenge was processing payments safely at booking velocity — decoupling Adyen settlement from the synchronous booking response so a checkout can retry without ever double-charging, while keeping read-heavy availability queries off the write primary.
Context
The Problem
Rental bookings couple a time-sensitive availability decision with a payment that must be exactly-once. A naive synchronous flow ties the booking response to third-party payment latency and makes retries dangerous — a timeout on the client side can trigger a second charge.
Constraints
- Payment settlement must be idempotent and retry-safe across client timeouts and webhook redelivery.
- Read-heavy availability and reporting queries must not contend with booking writes.
- Availability has to stay consistent across regions despite asynchronous replication lag.
- Booking amendments and cancellations require an auditable, compliance-grade trail.
- Code quality must hold at team velocity across a large, long-lived Symfony codebase.
Scope
Requirements
Functional
- ▹Booking core with availability sync via an external allotment scheduler
- ▹Fleet management: cars, car events, servicing, damage and imagery
- ▹Payment capture and reconciliation via Adyen with webhook notifications
- ▹Customer accounts, profiles and rental contract lifecycle
- ▹Operations: invoicing, reimbursement, cleaning and car-event workflows
- ▹Integrations with Zendesk, Mailchimp, Segment and Airtable dynamic pricing
- ▹REST API (FOS REST Bundle) with JWT auth and CORS for dual frontends
Non-functional
- ▹High availability via master-slave MySQL replication
- ▹Read scaling — analytics and availability reads served from replicas
- ▹Idempotent payment processing decoupled through an async queue
- ▹Multi-region deployment across EU and SG with per-region storage
- ▹Distributed error tracking and structured logging
- ▹CI/CD quality gates: PHPStan L8, PHPCS, SonarCloud, coverage tracking
System Design
Architecture
Payment Queue
Symfony Messenger async_pec consumers move Adyen capture and settlement off the booking request path, enabling bounded retries with memory and time limits so a stuck job never hangs a worker.
Master-Slave MySQL
Writes target the primary while availability, reporting and analytics reads are served from async replicas, isolating read churn from booking writes.
Booking Event Sourcing
Booking to BookingEvent state transitions and Blameable audit traits give a reproducible, compliance-grade trail for amendments and cancellations.
Availability Sync
An external allotment scheduler feeds real-time inventory; replica lag is masked with cache TTLs so cross-region availability stays consistent enough for rental ops.
Integration Layer
Adyen, Zendesk, Mailchimp, Segment and Airtable dynamic pricing sit behind the API in dedicated service namespaces for independent testing and scaling.
Stack
Technology Stack
Backend
Data
Async / Integrations
Infra / CI
Persistence
Data Model
- Booking (MySQL): reservation core, dates, region, contract linkage, soft-delete versioning
- BookingEvent (MySQL): state-machine transitions forming the booking audit trail
- Car / CarEvent (MySQL): fleet inventory, servicing, damage and event history
- Payment (MySQL): Adyen references, capture state, reconciliation, idempotency keys
- Customer / User (MySQL): accounts, profiles, roles, Blameable audit fields
- Spatial data (MySQL): creof spatial types for location-based availability queries
Reasoning
Key Decisions & Trade-offs
Master-slave MySQL replication
trade-off
Async replication adds read lag and eventual consistency, but read replicas absorb analytics and availability load without contending with the write primary.
Async payment queue via Symfony Messenger
trade-off
Extra queue-lag complexity, but decouples payment settlement from the booking response and makes retries idempotent instead of double-charging.
Strict PHPStan L8 + PHPCS + SonarCloud gates
trade-off
Slower onboarding and stricter discipline, but null-reference and regression bugs are caught before staging on a large, long-lived codebase.
Kubernetes-capable deployment alongside AWS ECS
trade-off
Per-region KUBECONFIG and added operational overhead, but multi-cloud-capable, regionally isolated deployments across EU and SG.
Operations
Scaling & Reliability
Horizontal: stateless PHP-FPM tasks on ECS Fargate auto-scale on CPU and memory, deployed per region behind the gateway.
Reads vs writes: the MySQL master handles booking writes while async replicas serve availability, reporting and analytics; cache TTLs mask replication lag for inventory reads.
Payments: Messenger persists jobs to the database and processes them on consumers with memory and time limits; combined with Adyen webhooks this yields safe, bounded retries.
Resilience: Redis for sessions and counters, per-region S3 CDN for static assets, Sentry for distributed error tracking, and PHPStan L8 / PHPCS / SonarCloud gates preventing regressions from shipping.
What made this hard
Architect's Challenges
- →Idempotent payments: Adyen webhook plus async queue enable safe retry without double-charging
- →Multi-region availability consistency across async read replicas and the allotment scheduler
- →Booking-state correctness and compliance via event-sourced BookingEvent audit trails
- →Sustaining code quality at velocity with automated PHPStan, PHPCS and SonarCloud gates
Results
Outcome
- ▹Queue-based payment processing scales independently of booking throughput
- ▹Enforced quality gates: PHPStan L8, PHPCS, SonarCloud, coverage tracking per PR
- ▹Blue-green ECS deployments with a fast rollback path
- ▹{{TODO: add real metrics — uptime, booking volume, payment throughput, MTTR}}