Personal project · SaaS · In development

A rental SaaS designed multi-tenant from the first module.

Nexora Rent is a personal project: one workspace for car-rental agencies, with data isolated per agency. The operations back-office is largely in place; card payments and 2FA are still open.

StatusPersonal project · in progress
RoleProduct design · full-stack
StackPHP 8.3 · Symfony 7.4 · MySQL

Surface 01

Agency workspace

Twig operations UI: fleet, customers, reservations, rentals, contracts, payments, invoicing and maintenance.

Surface 02

SaaS back-office

EasyAdmin for the platform operator: tenants, plans, accounts and follow-up — kept apart from the agency app.

IsolationMulti-tenant · Doctrine filter
QualityPHPUnit · PHPStan · GitHub Actions
DeliveryDocker · GitHub Actions

Screenshots of the Twig workspace, on a demonstration tenant. The product is not frozen.

Business problem

Vehicles, customers, contracts and payments often live in spreadsheets and disconnected tools. An agency needs one workspace, while staying isolated from other agencies on the same application.

Architecture

A modular monolith, split into bounded contexts (Domain / Application / Infrastructure / UI). Multi-tenancy is not an add-on: every business record belongs to an agency, a Doctrine filter isolates queries, and the platform back-office bypasses that filter on purpose.

  1. TenantsIsolated agencies (shared schema, tenant_id, subdomain or account)
  2. DomainsFleet · Customers · Reservations · Rentals · Contracts · Payments · Invoicing · Maintenance
  3. ApplicationPHP 8.3 · Symfony 7.4 LTS · Doctrine ORM 3 · Messenger · Redis
  4. UITwig + Stimulus (agency) · EasyAdmin 5 (platform) · Dompdf · email

Strengths

Multi-tenant in the model

Isolation via a Doctrine filter and request context. An agent only sees their agency; the SaaS operator manages tenants separately.

Real permissions, not a single ROLE_ADMIN

Agency roles (owner, manager, agent, accountant, fleet) and per-domain permissions, enforced with voters.

Readable domain split

Each context has its own folder. Contracts, fleet or SaaS billing do not share a catch-all Controller/ dump.

A product-grade quality bar

PHPStan, PHP CS Fixer, PHPUnit, Twig/YAML lint, GitHub Actions (MySQL + Redis), Docker Compose and a healthcheck.

Still open

A personal project, not a closed commercial SaaS. The agency back-office (day-to-day operations) is largely in place. Card payments, two-factor authentication and check-in / check-out inspections are still to do — stated here on purpose.

My role

Solo: product, data model, Symfony, agency UI, back-office, tests and CI.

Deliverables and results

Delivered as part of my mission

  • Agency back-office in place: dashboard, planning, reservations, rentals, contracts, fleet, customers, payments, invoices, maintenance, reports and settings.
  • Current stack: PHP 8.3, Symfony 7.4, MySQL, Redis, EasyAdmin 5.
  • CI: static analysis, tests and Doctrine schema checks.
  • Still open: card payments, 2FA and check-in / check-out inspections.

Stack

Backend

  • PHP 8.3
  • Symfony 7.4
  • Doctrine ORM 3
  • MySQL 8
  • Messenger
  • Redis

Front

  • Twig
  • EasyAdmin 5
  • Stimulus
  • Turbo
  • Bootstrap 5

Delivery

  • Docker
  • PHPUnit
  • PHPStan
  • GitHub Actions
  • Dompdf

Other case studies