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)