# WEBX Travel — Architecture Overview

WEBX Travel is a **multi-tenant SaaS** for travel agencies, built as a **modular
monolith**: one codebase, one central database, and **one independent database
per tenant**. Amaka Tours is simply the first tenant — never a special case in
code.

- **Backend:** Laravel 12 (PHP 8.2+; built/tested on 8.3, runs on 8.4 in prod)
- **Frontend:** Vue 3 + TypeScript + Vite + Tailwind CSS (compiled to static
  assets; the server needs no Node at runtime)
- **API:** `/api/v1` with Laravel Sanctum
- **Database:** MySQL 8 / MariaDB in production, SQLite for local dev & tests
- **Queues:** `database` driver, driven by cron (`queue:work --stop-when-empty`)
- **Files:** Laravel Filesystem, private per tenant (`tenants/{uuid}/…`)

> **Security note.** The original prompt targeted Laravel 10 / PHP 8.1. During
> setup we found (a) the hosting actually offers PHP 8.4, and (b) Composer now
> **refuses to install Laravel 10** because every 10.x release carries unpatched
> security advisories. We therefore build on **Laravel 12**, a fully supported,
> vulnerability-free stack, keeping every architectural decision from the prompt
> intact. See [decisions.md](decisions.md).

## Module map

Code is organized by domain under `app/Modules/*`. Only the foundation modules
carry logic in this phase; the rest are placeholders for later phases.

| Module | Phase 01 role |
|---|---|
| `Platform` | Central catalog models (tenants, domains, plans, subscriptions, audit) |
| `Tenancy` | Tenant resolution, connection management, jobs, provisioning, commands |
| `Identity` | Tenant `User` + Sanctum token model (tenant-scoped) |
| `Configuration` | `TenantSetting` (minimal) |
| `Operations` | Tenant health-check model, tenant context endpoints, tenant-aware job |
| `Shared` | Base models, request-id middleware, uniform API responses |
| `CRM`, `Sales`, `Tours`, `Lodging`, `Finance`, `Suppliers`, `Documents`, `Reporting` | Reserved for later phases |

## Two connections

`config/database.php` defines two logical connections:

- **`central`** — the platform catalog. Holds *only* SaaS metadata: tenants,
  domains, encrypted tenant credentials, plans, features, subscriptions,
  platform users and audit logs. Never tenant operational data.
- **`tenant`** — a **template** with `null` credentials. At request/job time,
  `App\Modules\Tenancy\Database\TenantConnectionManager` fills it from the
  tenant's **encrypted** credentials in the central DB, then purges the pool so
  the next query uses the right database.

See [multi-tenancy.md](multi-tenancy.md) for the full request lifecycle.

## Request lifecycle (tenant route)

```
HTTP request
  → AssignRequestId (correlation id on every request)
  → prevent-central (reject WEBX platform hosts)
  → tenant  (InitializeTenancyByDomain):
        1. resolve tenant by server host (NEVER client input)
        2. assert domain active + tenant active + subscription serviceable
        3. open tenant DB connection + private filesystem
  → controller  (reads/writes the tenant database)
  → terminate: tear down tenant context (no leak to next request)
```

## Migrations: two families

- `database/migrations/core/` → central schema, run by **`php artisan core:migrate`**
- `database/migrations/tenant/` → tenant schema, run by **`php artisan tenant:migrate {uuid|--all}`**

The commands hardcode connection + path, so core migrations can never run
against a tenant database and vice versa (verified by `MigrationSeparationTest`).

## Identifiers & money

- Public identifier: **ULID** (`tenants.uuid`) — stable, used in URLs/jobs/files.
- Technical primary key: `BIGINT` (internal only).
- Tenant **database name** derives from a short `technical_id`, never the
  commercial name.
- Money is always `DECIMAL` + ISO currency, never float. Timestamps are UTC.

## Tests

Run `php artisan test`. The suite covers tenant data isolation, host/domain
rejection, `tenant_id` spoofing, job connection isolation, migration separation,
and credential secrecy. See the `tests/Feature/Tenancy` directory.
