Managing route definitions in a large Laravel application can quickly become overwhelming—especially when dealing with complex modules like admissions, academics, and user management.
To ensure long-term maintainability, I recently refactored our admin route setup into a well-structured and modular format.
In this post, I’ll share how we transitioned from a monolithic route group to a fully modular setup and what benefits it brings.
🚧 The Challenge: Growing Route Complexity
Initially, all admin-related routes were grouped inside a massive closure in web.php
. This approach worked fine at first, but as the system evolved:
- The route file grew to hundreds of lines.
- Unrelated concerns (e.g., admission vs programme) were tangled together.
- Collaboration became error-prone due to constant merge conflicts.
We needed a better structure.
🧭 The Strategy: Structured Route Definitions
Instead of stuffing everything into web.php
, we organised our routes into dedicated files under a routes/web/admin
directory.
✅ Step 1: Central Admin Route Loader
// routes/web/admin.php
use Illuminate\Support\Facades\Route;
Route::group([
'middleware' => ['is_admin', 'auth', 'verified'],
'prefix' => 'admin', 'as' => 'admin.',
], function () {
require_all_in(base_path('routes/web/admin/*.php'));
});
This loads all files matching admin/*.php
, keeping the main file clean.
✅ Step 2: Nested Setup Group
Setup-related routes are more detailed, so we pushed them further into a subdirectory:
// routes/web/admin/setup.php
use Illuminate\Support\Facades\Route;
Route::group(['prefix' => 'setup', 'as' => 'setups.'], function () {
require_all_in(base_path('routes/web/admin/setup/*.php'));
});
This allows better organisation and logical grouping of setup domains like CRM, programme, admission, etc.
📁 Final Directory Layout
routes/
└── web/
├── admin.php # Loads all admin route groups
└── admin/
├── academics.php
├── admissions.php
├── prospective-student.php
├── users.php
├── impersonate.php
├── governance.php
├── excel.php
├── profile.php
├── misc.php
├── document-template.php
├── letter.php
├── setup.php # Loads all setup submodules
└── setup/
├── admission.php
├── crm.php
├── programme.php
├── residential.php
├── student-record.php
└── common.php
🔍 Verifying the Structure
After restructuring, I dumped the route list before and after, then compared them programmatically.
Outcome:
- ✅ No routes were removed.
- ✅ No duplication or misrouting occurred.
- ✅ Route paths, names, and middleware stacks stayed intact.
🛠 Bonus
Dump routes before and after refactor
You may wan to dump the before refactor routes and after refactor the routes:
php artisan route:list --json > routes/route-ori.json
Then after refactor:
php artisan route:list --json > routes/route-refactor.json
Grab those two JSON files, and dump to ChatGPT / Claude to help you analyse and compare differences in both route files.
If there's missing routes, you may double check and add back those missing routes.
The require_all_in()
Helper
To autoload all files in a directory, I used this helper:
function require_all_in($path) {
foreach (glob($path) as $filename) {
require_once $filename;
}
}
This keeps each route group isolated and ensures automatic loading without manual imports.
🚀 Why This Matters
This restructuring significantly improved:
- Readability: Route responsibilities are clearly separated.
- Modularity: Easy to locate and modify specific feature routes.
- Team Collaboration: Developers can work independently in their modules.
- Future Growth: Adding new route groups doesn’t clutter core files.
🏁 Conclusion
A well-structured route directory makes a huge difference in large Laravel applications. If you're managing more than a few modules, modularising and grouping your routes should be a standard practice.
It’s a small change with massive long-term impact.
Top comments (0)