Skip to content

Architecture Overview

This page is the map of the whole system. Read it before diving into any single service; each section links to the detailed page.

Volāre is a monorepo with two products:

  • The Web (byvolare.com) — the customer-facing online travel agency, an Astro site where travellers browse trips and buy them.
  • Arkana (arkana.byvolare.com) — the Laravel + FilamentPHP back office where the team configures everything, and which serves the REST API the Web consumes.
flowchart TB
  subgraph Clients
    C[Customer browser]
    S[Staff browser]
    AI[AI tools<br/>Claude, Hermes]
  end

  subgraph AWS["AWS us-east-2 (defined in infra-volare)"]
    WEB[The Web<br/>Astro SSR on front servers]
    subgraph Laravel["Arkana (Laravel 13)"]
      API[REST API<br/>/api]
      ADMIN[Filament back office<br/>/admin]
      MCP[Arkana MCP<br/>/mcp/arkana]
      Q[Queue worker + scheduler<br/>ops-runner]
    end
    DB[(PostgreSQL 18 on RDS<br/>data, cache, sessions, queue)]
    S3[(S3 + CloudFront<br/>assets)]
  end

  EXT[External services<br/>AerTicket, Stripe, Redsys, Intermundial,<br/>Pipedrive, NetSuite, Resend…]

  C --> WEB --> API
  S --> ADMIN
  AI --> MCP
  API --> DB
  ADMIN --> DB
  MCP --> DB
  Q --> DB
  API --> EXT
  Q --> EXT
  ADMIN --> S3

Where each box runs in production, and how traffic is routed, is described in Infrastructure Overview.

  • The Web never touches the database. It renders pages server-side and calls the Arkana REST API with a service token (INTERNAL_API_TOKEN, a Sanctum token). Customers’ own sessions (trip page, my account) use passwordless magic links — see Client Authentication.
  • Prices are computed only in the backend. The Web displays the price the API returns and never recalculates it.
  • Staff use Arkana at /admin, signing in with Google (allow-listed company domains) — see Google OAuth.
  • AI agents use the Arkana MCP server, authenticated with OAuth (Laravel Passport) or an agent token — see Arkana MCP.

Arkana is organised by responsibility, and business rules live in exactly one place:

Layer Where Rule
Entry points app/Http/Controllers (API), app/Filament (back office), app/Mcp (MCP tools), app/Console/Commands Thin: validate input, call a service, shape the output
Business logic app/Services/<Domain> The single source of every rule (pricing, availability, booking lifecycle…)
Background work app/Jobs, routes/console.php (schedule) Jobs call services; they do not re-implement logic
Data app/Models, database/migrations Eloquent models, enums in app/Enums, authorization in app/Policies
AI app/Ai laravel/ai agents and tools (OpenRouter by default) — see AI System

Request flow for an API call: route → middleware → controller → Form Request validation → service → models → API Resource (JSON).

flowchart LR
  SUP[Supplier] --> TOUR[SupplierTour]
  TOUR --> PT[ProductTemplate]
  PT --> PBM[ProductByMarket]
  PBM --> FC[Flight config + legs]
  FC --> CACHE[Dynamic flight cache]
  PBM --> OFF[Offer<br/>one departure date]
  CACHE --> OFF
  OFF --> BK[Booking]
  BK --> PAX[Passengers]
  BK --> PAY[Payments]
  BK --> FB[Flight bookings<br/>AerTicket]
  • A SupplierTour owns its ProductTemplate (the trip content). Publishing it to a market creates a ProductByMarket with translations and flight configuration.
  • The flight cache pre-fetches fares from AerTicket for each configured route and date.
  • Offers combine a ProductByMarket, a departure date, cached flights and the supplier land rate into a sellable, priced package.
  • A Booking is created from an offer at checkout and carries passengers, payments, upsells and the flight bookings issued with AerTicket.

Details: Products System, Product Entity Relationships, Offers, Bookings.

Everything stateful is in PostgreSQL:

Concern Driver Notes
Application data PostgreSQL 18 RDS in production; full-text search for airports
Cache database No Redis in production
Sessions database
Queues database (jobs table) See below
Files S3 (+ CloudFront) Uploaded images and documents

Redis runs in the local Sail stack but the application does not depend on it.

Jobs are grouped into named queues so slow work cannot block urgent work. Queue order is priority:

Queue What runs there
aerticket-tickets, aerticket-fastlane Ticket issuance
aerticket-bookings, aerticket-voids Flight bookings and voids
pipedrive-sync, activecampaign-sync CRM and newsletter sync
offer-recalculations, offer-generation Offer price sweeps and generation
flight-searches Filling the flight cache
default Emails, notifications, everything else

Locally, flight-searches has its own worker (queue-flights) and the rest share queue. In production a single queue-worker container on the ops-runner consumes all of them in that order. Scheduled tasks run in the scheduler container next to it. See Queue System.

Service Used for Docs
AerTicket Flight search, booking, ticketing AerTicket
Stripe (Laravel Cashier) and Redsys Card payments Payment
Intermundial Travel insurance Insurance
Signaturit E-signature of supplier contracts Signaturit
Pipedrive CRM (leads, deals) Pipedrive
NetSuite ERP / accounting NetSuite
ActiveCampaign Newsletter Newsletter Subscriptions
Resend Transactional email Notifications
Google Places Points of interest Google Places
Exchange-rate providers Supplier currency conversion Currency Exchange
OpenRouter (and Anthropic, Gemini, OpenAI) AI content tools AI System
Google OAuth Staff sign-in Google OAuth
Google Tag Manager, GA4, Clarity Web analytics GA4 Ecommerce Tracking

The Web is server-rendered Astro with React islands for interactive parts (checkout, search). frontend/astro.config.mjs selects the adapter:

  • Default / production on AWS: @astrojs/node (standalone), running in the front container.
  • Cloudflare previews (DEPLOY_TARGET=cloudflare): @astrojs/cloudflare, with sessions in KV.

See Frontend Overview.

Tool Purpose
Grafana Cloud Server and container metrics, logs, production alerts — Monitoring and Alerts
Laravel Nightwatch Request, job and query performance in production
Sentry Exceptions
Spatie Health (/health, /health-dashboard) Application self-checks used by the load balancer — Health Monitoring
Telescope Local debugging (production: ops-runner, Twingate only)