Learning Commerce
NUMO.SA
Large Laravel 9 learning-commerce monolith for NUMO: separate route modules for the public NUMO site, branch-scoped storefronts (`/b/{branch}` with branch middleware), the teacher or learner platform area, and a heavy Livewire-powered Board back-office—plus a Sanctum-backed API v1, Breeze auth, Spatie Permission and Translatable, cart and checkout (Moyasar, Tabby), Zoom and Vimeo, Maatwebsite Excel, Horizon on Redis, S3 via Flysystem, and Vapor-oriented deployment packages.
Problem
What was broken
A national training brand needs more than a brochure site: course discovery, branch-specific offers, carts and installments, teacher workflows (projects, discussions, attendance exports), certificate verification, and operations dashboards—all without fragmenting into unrelated apps that drift out of sync.
Solution
How we fixed it
One Laravel application registers multiple web route groups (`numo.php`, `branch.php`, `platform.php`, `board.php`) from `RouteServiceProvider`, each backed by dozens of Livewire components for catalog, checkout, teacher tools, and board administration. Payments integrate with Moyasar and Tabby; media and documents lean on S3; long work runs through Horizon and Redis; APIs expose branches, courses, payments, and user forms for integrated clients.
Challenges
What made this hard
Branch isolation (middleware, insurance checks, Tabby return URLs) must not leak data across tenants. Board Livewire tables and Excel exports must stay performant on wide user and payment datasets. Teacher platform flows (projects, discusses, certificate OTP) need consistent authorization with the rest of the app.
Architecture
How the system is shaped
API Structure
`routes/api.php` exposes versioned JSON under `/api/v1` (branches, courses, payments, user forms) alongside resource-style endpoints for American Board and shipping-certificate payments. The main UX remains session-based web plus Livewire; Sanctum secures token access where needed.
Data Flow
Learner or staff request → middleware (locale, branch, teacher, or board context) → controllers or Livewire components → Eloquent models and domain services → MySQL. File uploads and certificates route to S3; notifications and realtime UI can use Pusher; heavy exports and notifications enqueue through Horizon.
Backend Decisions
Kept a monolith with clear route file boundaries instead of premature microservices: shared auth (Breeze), shared models, and Spatie roles across surfaces. Chose first-party Laravel queue and cache primitives with Horizon for visibility; payment and BNPL providers stay behind service-style wrappers so checkout templates do not embed gateway details.
Decisions
Why this stack and shape
Livewire 2 for dense admin and checkout UX; Laravel Breeze for account flows; Spatie Permission plus Translatable for Arabic or English content; Redis + Horizon over ad hoc cron for retries; S3 + Vapor core for cloud-friendly assets and config.