Files
fuel-price/.claude/rules/architecture.md
Ovidiu U d5fb7f85bd
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (8.3) (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled
feat: add Filament admin panel with migrations and design spec
- Add AdminPanelProvider mounting panel at `/admin` with `is_admin` auth guard
- Add `is_admin` boolean column to users table
- Add brent_prices and price_predictions tables with appropriate indexes
- Add comprehensive admin design spec covering resources, dashboard, navigation, and build order
- Configure default panel with amber primary color and standard middleware stack
- Add compiled Filament assets (actions.js, app.css)
2026-04-04 13:40:56 +01:00

2.8 KiB

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/.