# Architecture ## Core principle: fat Services, thin everything else All business logic lives in Service classes. Controllers, Livewire components, and console commands are thin orchestrators — they call Services and return results. This keeps the app API-extractable later without a rewrite. ## Directory structure ``` app/ ├── Console/Commands/ # Scheduler commands (PollFuelPrices, PredictOilPrices, RunScoringEngine) ├── Http/Controllers/ # Minimal — auth + Stripe webhook only ├── Livewire/ # Classic two-file Livewire components ├── Models/ # Eloquent models ├── Notifications/ # Laravel Notification classes (multi-channel) ├── Services/ # ALL business logic lives here │ ├── FuelPriceService.php # Fuel Finder API polling + storage │ ├── AlertScoringService.php # Fill-up timing recommendation engine │ ├── StationTaggingService.php # Supermarket brand detection │ ├── NotificationDispatchService.php # Tier-aware notification routing │ ├── SubscriptionService.php # Cashier/tier helpers │ ├── PostcodeService.php # Resolves postcodes/outcodes/place names → lat/lng │ ├── OilPriceService.php # FRED fetch + EWMA/LLM Brent crude prediction │ ├── LocationResult.php # DTO returned by PostcodeService │ └── ApiLogger.php # Wraps all outbound HTTP calls, logs to api_logs └── Jobs/ # Queued jobs (dispatch notifications per user) resources/views/ ├── livewire/ # Livewire Blade templates └── emails/ # Mailable templates routes/ ├── web.php # All web routes (Livewire pages) └── api.php # Empty for now — API added later if needed ``` ## Service class conventions - Constructor injection only — no facade usage inside Services - Services are bound in AppServiceProvider if they need interfaces - Each Service has one responsibility — do not merge concerns - Return typed DTOs or Eloquent collections — never raw arrays from Services - Services never dispatch jobs directly — that's the controller/command's job ## No API yet `routes/api.php` stays empty for v1. Do not create API controllers or Sanctum token auth. The app is Livewire-only until subscriber count justifies a native app. When the API is added later, it will reuse the same Service classes. ## Livewire components (classic only) Use two-file classic Livewire components. Do NOT use Volt single-file syntax. Volt files from the starter kit (auth screens) are left as-is — do not convert them. New components go in `app/Livewire/` with corresponding Blade in `resources/views/livewire/`.