# Module System The module system provides automatic discovery and lazy loading of modules based on lifecycle events. Modules are self-contained units of functionality that can hook into the framework at specific points. ## Overview Traditional Laravel applications use service providers which are all loaded on every request. The Core module system: - **Auto-discovers** modules by scanning directories - **Lazy-loads** modules only when their events fire - **Caches** module registry for performance - **Supports** multiple module types (Mod, Plug, Website) ## Creating a Module ### Using Artisan ```bash # Create a standard module php artisan make:mod Blog # Create a website module php artisan make:website Marketing # Create a plugin module php artisan make:plug Stripe ``` ### Manual Creation Create a `Boot.php` file in your module directory: ```php 'onWebRoutes', AdminPanelBooting::class => 'onAdminPanel', ConsoleBooting::class => 'onConsole', ]; /** * Register public web routes */ public function onWebRoutes(WebRoutesRegistering $event): void { $event->views('blog', __DIR__.'/Views'); $event->routes(fn () => require __DIR__.'/Routes/web.php'); } /** * Register admin panel routes and menus */ public function onAdminPanel(AdminPanelBooting $event): void { $event->menu('blog', [ 'label' => 'Blog', 'icon' => 'newspaper', 'route' => 'admin.blog.index', 'order' => 20, ]); $event->routes(fn () => require __DIR__.'/Routes/admin.php'); } /** * Register console commands */ public function onConsole(ConsoleBooting $event): void { $event->commands([ Commands\PublishPostsCommand::class, Commands\ImportPostsCommand::class, ]); } } ``` ## Directory Structure ``` Mod/Blog/ ├── Boot.php # Module bootstrap ├── Actions/ # Business logic │ ├── CreatePost.php │ ├── UpdatePost.php │ └── DeletePost.php ├── Controllers/ │ ├── Web/ │ │ └── PostController.php │ └── Admin/ │ └── PostController.php ├── Models/ │ ├── Post.php │ └── Category.php ├── Routes/ │ ├── web.php │ ├── admin.php │ └── api.php ├── Views/ │ ├── web/ │ └── admin/ ├── Database/ │ ├── Migrations/ │ ├── Factories/ │ └── Seeders/ ├── Tests/ │ ├── Feature/ │ └── Unit/ └── Lang/ └── en_GB/ ``` ## Lifecycle Events Modules can hook into these lifecycle events: ### WebRoutesRegistering Register public-facing web routes: ```php public function onWebRoutes(WebRoutesRegistering $event): void { // Register views $event->views('blog', __DIR__.'/Views'); // Register translations $event->lang('blog', __DIR__.'/Lang'); // Register routes $event->routes(function () { Route::get('/blog', [PostController::class, 'index']); Route::get('/blog/{slug}', [PostController::class, 'show']); }); } ``` ### AdminPanelBooting Register admin panel routes, menus, and widgets: ```php public function onAdminPanel(AdminPanelBooting $event): void { // Register admin menu $event->menu('blog', [ 'label' => 'Blog', 'icon' => 'newspaper', 'route' => 'admin.blog.index', 'order' => 20, 'children' => [ ['label' => 'Posts', 'route' => 'admin.blog.posts'], ['label' => 'Categories', 'route' => 'admin.blog.categories'], ], ]); // Register routes $event->routes(fn () => require __DIR__.'/Routes/admin.php'); } ``` ### ApiRoutesRegistering Register REST API endpoints: ```php public function onApiRoutes(ApiRoutesRegistering $event): void { $event->routes(function () { Route::get('/posts', [Api\PostController::class, 'index']); Route::post('/posts', [Api\PostController::class, 'store']); Route::get('/posts/{id}', [Api\PostController::class, 'show']); }); } ``` ### ClientRoutesRegistering Register authenticated client routes: ```php public function onClientRoutes(ClientRoutesRegistering $event): void { $event->routes(function () { Route::get('/dashboard/posts', [Client\PostController::class, 'index']); Route::post('/dashboard/posts', [Client\PostController::class, 'store']); }); } ``` ### ConsoleBooting Register Artisan commands: ```php public function onConsole(ConsoleBooting $event): void { $event->commands([ Commands\PublishPostsCommand::class, Commands\GenerateSitemapCommand::class, ]); $event->schedule(function (Schedule $schedule) { $schedule->command('blog:publish-scheduled') ->everyFiveMinutes(); }); } ``` ### McpToolsRegistering Register MCP (Model Context Protocol) tools: ```php public function onMcpTools(McpToolsRegistering $event): void { $event->tool('blog:create-post', Tools\CreatePostTool::class); $event->tool('blog:list-posts', Tools\ListPostsTool::class); } ``` ### FrameworkBooted Late-stage initialization after all modules loaded: ```php public function onFrameworkBooted(FrameworkBooted $event): void { // Register macros, observers, policies, etc. Post::observe(PostObserver::class); Builder::macro('published', function () { return $this->where('status', 'published') ->where('published_at', '<=', now()); }); } ``` ## Module Discovery The framework automatically scans these directories: ```php // config/core.php 'module_paths' => [ app_path('Core'), // Core modules app_path('Mod'), // Standard modules app_path('Website'), // Website modules app_path('Plug'), // Plugin modules ], ``` ### Custom Namespaces Map custom paths to namespaces: ```php use Core\Module\ModuleScanner; $scanner = app(ModuleScanner::class); $scanner->setNamespaceMap([ '/Extensions' => 'Extensions\\', '/Custom' => 'Custom\\Modules\\', ]); ``` ## Lazy Loading Modules are only instantiated when their events fire: 1. **Scan Phase** - `ModuleScanner` finds all `Boot.php` files 2. **Registry Phase** - `ModuleRegistry` wires lazy listeners 3. **Event Phase** - Event fires, `LazyModuleListener` instantiates module 4. **Execution Phase** - Module method is called **Performance Benefits:** - Modules not used in CLI don't load in CLI - Admin modules don't load on public requests - API modules don't load on web requests ## Module Registry View registered modules and their listeners: ```php use Core\Module\ModuleRegistry; $registry = app(ModuleRegistry::class); // Get all registered modules $modules = $registry->all(); // Get modules for specific event $webModules = $registry->forEvent(WebRoutesRegistering::class); ``` ## Module Cache Module discovery is cached for performance: ```bash # Clear module cache php artisan cache:clear # Or specifically php artisan optimize:clear ``` **Cache Location:** `bootstrap/cache/modules.php` ## Module Dependencies Modules can declare dependencies using service discovery: ```php use Core\Service\Contracts\ServiceDefinition; use Core\Service\Contracts\ServiceDependency; class Boot implements ServiceDefinition { public static array $listens = [ WebRoutesRegistering::class => 'onWebRoutes', ]; public function getServiceName(): string { return 'blog'; } public function getServiceVersion(): string { return '1.0.0'; } public function getDependencies(): array { return [ new ServiceDependency('media', '>=1.0'), new ServiceDependency('cdn', '>=2.0'), ]; } } ``` ## Testing Modules ### Feature Tests ```php 'Test Post', 'content' => 'Content here', ]); $this->assertDatabaseHas('posts', [ 'title' => 'Test Post', ]); $this->get("/blog/{$post->slug}") ->assertOk() ->assertSee('Test Post'); } } ``` ### Unit Tests ```php onWebRoutes($event); $this->assertTrue($event->hasRoutes()); } } ``` ## Best Practices ### 1. Keep Modules Focused ```php // ✅ Good - focused modules Mod/Blog/ Mod/Comments/ Mod/Analytics/ // ❌ Bad - monolithic module Mod/Everything/ ``` ### 2. Use Proper Namespacing ```php // ✅ Good namespace Mod\Blog\Controllers\Web; // ❌ Bad namespace App\Http\Controllers; ``` ### 3. Register Dependencies ```php // ✅ Good - declare dependencies public function getDependencies(): array { return [ new ServiceDependency('media', '>=1.0'), ]; } ``` ### 4. Only Hook Necessary Events ```php // ✅ Good - only web routes public static array $listens = [ WebRoutesRegistering::class => 'onWebRoutes', ]; // ❌ Bad - hooks everything public static array $listens = [ WebRoutesRegistering::class => 'onWebRoutes', AdminPanelBooting::class => 'onAdminPanel', ApiRoutesRegistering::class => 'onApiRoutes', // ... (when you don't need them all) ]; ``` ### 5. Use Actions for Business Logic ```php // ✅ Good $post = CreatePost::run($data); // ❌ Bad - logic in controller public function store(Request $request) { $post = Post::create($request->all()); event(new PostCreated($post)); Cache::forget('posts'); return redirect()->route('posts.show', $post); } ``` ## Learn More - [Lifecycle Events →](/core/events) - [Actions Pattern →](/core/actions) - [Service Discovery →](/core/services) - [Architecture Overview →](/architecture/module-system)