The architecture of a web framework profoundly influences how developers organize code, scale applications, and maintain projects over time. With the release of Laravel 12, Taylor Otwell's team introduced a radical revision of the default application structure, focusing on a minimal bootstrap and a flatter, more intuitive directory hierarchy. This guide examines the changes in detail, comparing them with Laravel 10 and 11, and explains how to adapt your workflow to fully leverage the new architecture. Whether you are upgrading an existing project or starting from scratch, understanding these transformations is essential to stay ahead in the Laravel ecosystem.
Overview of the New Laravel 12 Architecture
Laravel 12 abandons the traditional separation into app/Http, app/Models, app/Providers in favor of a completely flattened structure. The app directory now contains only the necessary subfolders: Controllers, Models, Middleware, Providers (a single stripped-down AppServiceProvider), and the new Actions folder for encapsulating business logic. The infamous app/Exceptions/Handler disappears, integrated directly into the application kernel. The routes/web.php file is also simplified, with the removal of the RouteServiceProvider and the adoption of convention-based automatic route loading.
The 'app' Directory Structure
In the minimal bootstrap of Laravel 12, the app directory looks like this:
app/
├── Actions/
├── Controllers/
├── Middleware/
├── Models/
├── Providers/
│ └── AppServiceProvider.php
└── enums/
There is no longer app/Http. Controllers reside directly in app/Controllers (with namespace App\Controllers instead of App\Http\Controllers). Models remain in app/Models. Middleware are grouped in app/Middleware and auto-registered via attributes. The AppServiceProvider now contains only the boot method with essential minimal configurations; everything else is delegated to specific providers generated with artisan make:provider.
Minimal Bootstrap: The New 'bootstrap/app.php'
The bootstrap/app.php file has been dramatically reduced. In Laravel 10 and 11 it contained the application instance creation, default service provider registration, and global middleware configuration. In Laravel 12, everything is resolved in a few lines thanks to auto-discovery:
<?php
use Illuminate\Foundation\Application;
return Application::configure()
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function ($middleware) {
// Custom or group middleware
})
->withExceptions(function ($exceptions) {
// Custom exception handling
})
->create();
There is no more app/Exceptions/Handler: exceptions are handled directly in the withExceptions closure. Similarly, global middleware is registered via withMiddleware. This approach reduces the number of files to create and maintain, moving configuration into the application entry point.
Differences from Laravel 10 and 11
From Laravel 10 to Laravel 12
Laravel 10 still used the classic structure with app/Http/Controllers, app/Http/Middleware, app/Providers, and app/Exceptions/Handler. Moving to Laravel 12 requires significant directory and namespace restructuring. Controllers must be moved from App\Http\Controllers to App\Controllers and namespaces updated in all files referencing them. The bootstrap/app.php file must be rewritten following the new pattern. Moreover, the RouteServiceProvider no longer exists: route registration happens directly in bootstrap/app.php or via the standard App\Providers\RouteServiceProvider (kept for backward compatibility but discouraged).
From Laravel 11 to Laravel 12
Laravel 11 had already taken a step toward simplification by removing some boilerplate files (like Kernel.php and Exceptions/Handler). However, it still retained the app/Http directory and the default RouteServiceProvider. With Laravel 12, the app/Http directory disappears completely. Middleware are now defined via PHP attributes, such as #[Middleware('auth')] in controllers, or registered globally in bootstrap/app.php. The old middleware alias system has been replaced by attributes or a new alias method inside the withMiddleware closure.
Comparison Table of Main Directories
Laravel 10 Laravel 11 Laravel 12
app/ app/ app/
├── Console/ ├── Console/ ├── Actions/
├── Exceptions/ ├── Exceptions/ ├── Controllers/
├── Http/ ├── Http/ ├── Middleware/
│ ├── Controllers/ │ ├── Controllers/ ├── Models/
│ ├── Middleware/ │ ├── Middleware/ ├── Providers/
│ └── Requests/ │ └── Requests/ └── enums/
├── Models/ ├── Models/
├── Providers/ └── Providers/
└── Providers/
Impact on Daily Development
The new structure reduces cognitive load for developers starting a Laravel project. It is no longer necessary to wonder where to place a controller or middleware: the answer is directly in the respective folders under app. Additionally, using attributes for middleware registration makes the code more declarative and easily testable. The minimal bootstrap results in faster application startup, as fewer files are autoloaded. Exception handling also becomes leaner: instead of creating an entire Handler class, a simple closure in bootstrap/app.php suffices.
For those coming from Laravel 10, the upgrade requires careful attention to details. It is advisable to use the laravel/upgrade tool to automate namespace and directory migrations. For those already on Laravel 11, the transition is less traumatic: most code remains valid, with only the removal of the app/Http directory and the update of the bootstrap/app.php file.
Best Practices for Adopting the New Structure
- Reorganize namespaces: update all references from
App\Http\ControllerstoApp\Controllersand so on. - Leverage PHP attributes: use
#[Middleware]and#[Route]for more explicit code. - Centralize configuration: keep middleware and exception closures in
bootstrap/app.php, avoiding unnecessary separate files. - Limit service providers: do not create superfluous providers; use
AppServiceProvideronly for essential bindings.
Practical Conclusion
Laravel 12 marks a turning point in the framework's design philosophy: less boilerplate, more clarity. The new application structure and minimal bootstrap not only simplify developers' lives but also make projects easier to understand for newcomers. If you are starting a new project, begin directly with Laravel 12 and embrace the flattened structure. If you are upgrading an existing project, plan the migration carefully, using official tools and testing every change. For a deeper dive into the entire Laravel 11 and 12 ecosystem, check out our definitive guide to architecture, advanced Eloquent ORM, and Livewire 3. Also, keep an eye on the latest PHP features, such as property hooks and typed class constants, which integrate perfectly with Laravel 12.
Sponsored Protocol