Skip to main content
Tripfy Africa is a Laravel 11 monolith that follows an MVC-with-service-layer pattern. This page explains the major architectural decisions and how the key subsystems fit together.

High-level overview

MVC with service layer

Controllers are thin. All business logic lives in service classes under app/Services/.
LayerResponsibility
ControllerParse the HTTP request, validate input, call a service, return a response
ServiceOrchestrate business operations — availability checks, price calculation, payment dispatch, notification dispatch
ModelEloquent model with relationships, scopes, and attribute casting
JobHeavy or async work dispatched to the queue (email sends, webhook processing, CSS generation)

Code organisation

app/
├── Http/
│   ├── Controllers/
│   │   ├── Api/          # Sanctum-authenticated API endpoints
│   │   ├── User/         # Traveler and guide web controllers
│   │   └── Admin/        # Admin panel controllers
│   └── Middleware/
├── Models/               # Eloquent models
├── Services/
│   ├── Gateway/          # One directory per payment gateway (37 gateways)
│   ├── SMS/              # SMS provider implementations
│   ├── Payout/           # Payout provider implementations
│   ├── Translate/        # Translation provider
│   ├── BasicService.php
│   ├── CurrencyLayerService.php
│   ├── GeminiService.php
│   ├── LocalizationService.php
│   └── OpenAiService.php
├── Jobs/                 # Background jobs
├── Events/               # Event definitions
├── Listeners/            # Event handlers
└── Helpers/
    └── helpers.php       # Global helper functions

User roles

The platform has three distinct user segments sharing a single database. Access is enforced by separate auth guards and middleware.
  • Traveler — browses packages, books tours, submits reviews, chats with guides.
  • Guide / vendor — creates and manages tour packages, accepts or rejects bookings, manages payouts. Requires KYC verification before full access.
  • Administrator — full platform control via /admin: approves packages and KYC submissions, configures payment gateways, edits notification templates, manages site appearance.
The kyc middleware blocks guide-specific routes until identity verification is approved by an admin.

Event-driven architecture

Laravel’s event system decouples side-effects from core operations. Controllers fire an event; multiple listeners respond independently.
// BookingController dispatches an event after a booking is confirmed
event(new BookingCreated($booking));

// Independent listeners registered in EventServiceProvider:
// - SendBookingConfirmationEmail
// - NotifyGuideOfNewBooking
// - UpdatePackageAvailability
// - CreateTransactionRecord
This pattern keeps the booking flow fast and makes each listener independently testable and replaceable.

API structure

All API routes are prefixed /api/v1 and return JSON. Rate limiting is declared in routes/api.php:
Endpoint groupAuth requiredRate limit
POST /api/v1/auth/registerNo10 req/min
POST /api/v1/auth/loginNo10 req/min
GET /api/v1/packagesNo60 req/min
GET /api/v1/destinationsNo60 req/min
GET /api/v1/bookings/availabilityNo60 req/min
GET /api/v1/user/profileSanctum token60 req/min
POST /api/v1/reviewsSanctum token30 req/min
POST /api/v1/bookings/{uid}/cancelSanctum token30 req/min

Booking flow

Payment webhook routes (/api/payment/{code}/{trx?}/{type?}) are unauthenticated — they are called directly by external payment providers.

Package approval flow

Guides can also use the Gemini AI integration (POST /user/generate-with-ai) to draft package descriptions and generate images before submission.

Caching strategy

The application supports multiple cache drivers, with Redis recommended for production.
Cache layerDriverPurpose
Application dataRedisComputed results, heavy query output
SessionsRedis (production) / file (dev)User session state across requests
Dynamic CSSFileGenerated theme CSS written to public/assets/
Set CACHE_DRIVER=redis and SESSION_DRIVER=redis in .env for production.

Queue system

Background jobs are dispatched to the queue to keep HTTP responses fast. Set QUEUE_CONNECTION=database (default) or redis (recommended for production). Key jobs dispatched by the system:
SendEmailJob                    — all outbound email
ProcessPaymentWebhookJob        — gateway IPN processing
GenerateDynamicCssJob           — theme CSS regeneration
UpdatePackageAvailabilityJob    — slot recalculation after booking changes
Start a queue worker in production:
php artisan queue:work --tries=3
For supervised persistence, configure Supervisor to keep the worker running.

Horizontal scaling

The application is designed to run on multiple servers behind a load balancer:
  • Stateless application — store sessions in Redis (SESSION_DRIVER=redis) so any node can serve any request.
  • Shared file storage — switch FILESYSTEM_DISK to s3 (AWS S3 or DigitalOcean Spaces) so uploaded files are accessible to all nodes.
  • Read replicas — Laravel’s database configuration supports separate read and write connections for MySQL.
  • Queue workers — run additional workers on separate servers; they pull from the same Redis/database queue.

Security architecture

Authentication

ContextMechanism
Web (traveler / guide)Laravel session guard with auth middleware
API (mobile / external)Laravel Sanctum bearer token
Admin panelSeparate admin guard

Security measures applied

ThreatMitigation
CSRFAll state-changing web routes use POST; Laravel CSRF middleware active on all web routes
IDOR (insecure direct object reference)Booking ownership verified before accept / reject / complete / refund
Brute-force loginthrottle:5,1 on POST /login
Coupon code leakagePOST /coupon/check (was GET — codes would appear in server logs)
Unauthenticated DB writes/trans route removed; /queue-work and /schedule-run moved behind admin auth
SQL injectionEloquent ORM used throughout; raw queries avoided
XSSInput sanitised via stevebauman/purify before persistence
Rate limiting on paymentsthrottle:10,1 on POST /make-payment
2FATOTP via pragmarx/google2fa available to all users
reCAPTCHAGoogle reCAPTCHA on registration and login forms

Build docs developers (and LLMs) love