Laravel Architecture Patterns
Agent Workflow (MANDATORY)
Before ANY implementation, spawn 3 agents in parallel, one Agent call each with a name:
- fuse-ai-pilot:explore-codebase - Analyze existing architecture
- fuse-ai-pilot:research-expert - Verify Laravel patterns via Context7
- mcp__context7__query-docs - Check service container and DI patterns
After implementation, run fuse-ai-pilot:sniper for validation.
Overview
Laravel architecture focuses on clean separation of concerns, dependency injection, and maintainable code organization. This skill covers everything from project structure to production deployment.
When to Use
- Structuring new Laravel projects
- Implementing services, repositories, actions
- Setting up dependency injection
- Configuring development environments
- Deploying to production
Critical Rules
- Thin controllers - Delegate business logic to services
- Interfaces in app/Contracts/ - Never alongside implementations
- DI over facades - Constructor injection for testability
- Files within
FUSE_SOLID_MAX_LINES - Split larger files per SOLID
- Environment separation - .env never committed
Architecture
app/
├── Actions/ # Single-purpose action classes
├── Contracts/ # Interfaces (DI)
├── DTOs/ # Data transfer objects
├── Enums/ # PHP 8.1+ enums
├── Events/ # Domain events
├── Http/
│ ├── Controllers/ # Thin controllers
│ ├── Middleware/ # Request filters
│ ├── Requests/ # Form validation
│ └── Resources/ # API transformations
├── Jobs/ # Queued jobs
├── Listeners/ # Event handlers
├── Models/ # Eloquent models only
├── Policies/ # Authorization
├── Providers/ # Service registration
├── Repositories/ # Data access layer
└── Services/ # Business logic
Reference Guide
Core Architecture
Configuration & Setup
Development Environments
Utilities & Tools
Advanced Features
Operations
Templates
Feature Matrix
| Feature |
Reference |
Priority |
| Service Container |
container.md |
High |
| Service Providers |
providers.md |
High |
| Directory Structure |
structure.md |
High |
| Configuration |
configuration.md |
High |
| Installation |
installation.md |
High |
| Octane (Performance) |
octane.md |
High |
| Sail (Docker) |
sail.md |
High |
| Artisan CLI |
artisan.md |
Medium |
| Deployment |
deployment.md |
Medium |
| Envoy (SSH) |
envoy.md |
Medium |
| Facades |
facades.md |
Medium |
| Contracts |
contracts.md |
Medium |
| Valet (macOS) |
valet.md |
Medium |
| Upgrade Guide |
upgrade.md |
Medium |
| Logging |
logging.md |
Medium |
| Errors |
errors.md |
Medium |
| Lifecycle |
lifecycle.md |
Medium |
| Filesystem |
filesystem.md |
Medium |
| Helpers |
helpers.md |
Low |
| Pennant (Flags) |
pennant.md |
Low |
| Context |
context.md |
Low |
| Processes |
processes.md |
Low |
| Concurrency |
concurrency.md |
Low |
| MCP |
mcp.md |
Low |
| Packages |
packages.md |
Low |
| Releases |
releases.md |
Low |
| Homestead |
homestead.md |
Low |
Quick Reference
Service Injection
public function __construct(
private readonly UserServiceInterface $userService,
) {}
Service Provider Binding
public function register(): void
{
$this->app->bind(UserServiceInterface::class, UserService::class);
$this->app->singleton(CacheService::class);
}
Artisan Command
php artisan make:provider CustomServiceProvider
php artisan make:command ProcessOrders
Environment Access
$debug = env('APP_DEBUG', false);
$config = config('app.name');
Laravel 13 Notes
Updated stack
- Symfony 7.4 and 8.0 supported side by side (HttpFoundation, Console, Mailer)
- PHP 8.3 minimum (8.2 dropped)
- pda/pheanstalk 7.x / 8.x required for the Beanstalk driver (5.x dropped)
- Current stable version: 13.33 (September 2026)
Cache::touch() API
New method to refresh the TTL without recomputing the value.
Cache::touch('user:123', 3600);
Cache::touch('user:123', now()->addHour());
Queue::route() for dynamic routing
See [[laravel-queues]] for declarative routing per job class (Queue::route(ProcessPodcast::class, connection: 'redis', queue: 'podcasts')) and Queue::forward() (13.26+).
Nested model boot → LogicException
Laravel 13 throws a LogicException when a model is instantiated during its own boot (new static() inside the model's boot() or a trait's boot*()). Move that logic out of the boot cycle.
What's new in 13.x (13.1 → 13.33)
php artisan dev (13.16+): runs the server, queue:listen, Pail and Vite in a single terminal; customizable via Illuminate\Foundation\DevCommands::artisan('horizon', 'horizon') in AppServiceProvider::boot(), listed via php artisan dev:list (requires Node 22.13+)
#[BindWhen(Impl::class, static fn () => ...)] (13.22+): conditional binding declared on the interface — requires PHP 8.5
- Enum (
UnitEnum) support in most managers (Cache::store(), Queue, Log, Mail, Auth::guard()…) (13.3 → 13.7)
- Guzzle 8 supported (13.26+)
Migration Laravel 12 → 13
| Topic |
Before (12) |
After (13) |
| PHP minimum |
8.2 |
8.3 |
| PHPUnit |
11 |
12 (13 supported) |
| Pest |
3 |
4 (5 with PHP 8.4+) |
| CSRF |
VerifyCsrfToken |
PreventRequestForgery (origin check via Sec-Fetch-Site) |
| Cache prefix |
underscore |
hyphens by default (set CACHE_PREFIX, REDIS_PREFIX, SESSION_COOKIE for backward compatibility) |
| Beanstalk |
pheanstalk 5.x |
pheanstalk 7.x / 8.x |
| Symfony |
7.x |
7.4 / 8.0 |
| Model boot |
tolerated |
nested instantiation during the model's boot → LogicException |
| Config |
— |
new cache.serializable_classes (allowlist hardening, default false) |
# Backward-compatible cache prefixes when upgrading from L12
CACHE_PREFIX=laravel_cache_
REDIS_PREFIX=laravel_database_
SESSION_COOKIE=laravel_session
// config/cache.php — deserialization hardening
'serializable_classes' => [
App\DTO\PaymentDto::class,
App\DTO\OrderDto::class,
],
Best Practices
DO
- Use
final readonly class for DTOs and Value Objects (PHP 8.3+)
- Inject via constructor promotion +
interface (dependency inversion)
- Log via
Context::add() to propagate metadata across jobs/requests
- Configure
serializable_classes in production
- Prefer
app(Contract::class) over App::make() (strict typing)
DON'T
- Instantiate a model during its own boot (
new static() inside boot() → LogicException in L13)
- Hardcode absolute paths (use
base_path(), storage_path())
- Mix Repository and Service (one per responsibility)
- Bypass the container with
new ConcreteClass()
- Ignore the cache prefix change when upgrading from L12