Skip to main content
Laravel Modular provides flexible namespace configuration that allows you to customize how your modules are organized and named. This is especially important if you plan to extract modules into standalone packages.

Configuration File

All namespace configuration is managed in config/app-modules.php:
return [
    'modules_namespace' => 'Modules',
    'modules_vendor' => null,
    'modules_directory' => 'app-modules',
    'tests_base' => 'Tests\\TestCase',
    'stubs' => null,
    'should_discover_events' => null,
];
Source: config/app-modules.php

Modules Namespace

The modules_namespace setting controls the root PHP namespace for all your modules.

Default Configuration

'modules_namespace' => 'Modules',
With this configuration, a module named Blog will use the namespace:
Modules\Blog\Models\Post

Custom Organization Namespace

It’s highly recommended to use your organization name as the namespace:
'modules_namespace' => 'Acme',
This makes extracting modules to packages easier:
Acme\Blog\Models\Post
Acme\Ecommerce\Models\Product
Acme\CRM\Models\Customer
Using your organization name as the namespace makes it easier to publish modules as standalone Composer packages later.

Impact on Module Structure

The namespace affects how classes are referenced throughout your application:
app-modules/Blog/
├── src/
│   ├── Models/
│   │   └── Post.php          // Acme\Blog\Models\Post
│   ├── Http/
│   │   └── Controllers/
│   │       └── PostController.php  // Acme\Blog\Http\Controllers\PostController
│   └── Providers/
│       └── BlogServiceProvider.php // Acme\Blog\Providers\BlogServiceProvider
└── composer.json

Modules Vendor

The modules_vendor setting controls the vendor name used in composer.json files within modules.

Default Behavior

'modules_vendor' => null, // Auto-generates kebab-case from namespace
When null, the vendor is automatically generated from modules_namespace:
  • Modulesmodules
  • Acmeacme
  • AcmeCorpacme-corp

Custom Vendor Name

'modules_vendor' => 'my-company',
This affects the module’s composer.json:
{
  "name": "my-company/blog",
  "autoload": {
    "psr-4": {
      "Acme\\Blog\\": "src/"
    }
  }
}

Impact on PhpStorm Integration

The vendor name is used when configuring PhpStorm to exclude module symlinks from indexing. Source: src/Support/PhpStorm/PhpFrameworkWriter.php:15-20
$namespace = config('app-modules.modules_namespace', 'Modules');
$vendor = config('app-modules.modules_vendor') ?? Str::kebab($namespace);
$module_paths = $this->module_registry->modules()
    ->map(function(ModuleConfig $module) use (&$config, $vendor) {
        return '$PROJECT_DIR$/vendor/'.$vendor.'/'.$module->name;
    });
The vendor name must be kebab-case (lowercase with hyphens) to follow Composer naming conventions.

Modules Directory

The modules_directory setting controls where modules are stored in your project.

Default Location

'modules_directory' => 'app-modules',
Modules are stored at the project root:
my-laravel-project/
├── app/
├── app-modules/      ← Modules here
│   ├── Blog/
│   ├── Ecommerce/
│   └── CRM/
├── config/
└── vendor/

Custom Directory

'modules_directory' => 'modules',
Or place modules inside app/:
'modules_directory' => 'app/Modules',
Changing the modules directory after creating modules requires updating module paths in various config files. It’s best to set this before creating your first module.
The default app-modules/ directory has several advantages:
  1. Alphabetical sorting: Appears before app/ in directory listings, making modules prominent
  2. Clear separation: Distinguishes modular code from traditional Laravel app code
  3. Convention: Follows the package’s recommended structure

Tests Base Class

The tests_base setting controls which TestCase class generated tests extend.

Default Configuration

'tests_base' => 'Tests\\TestCase',
Generated test files will extend your application’s base TestCase:
namespace Modules\Blog\Tests\Feature;

use Tests\TestCase;

class PostTest extends TestCase
{
    // Test methods
}

Custom Test Base

If you have a custom base test class:
'tests_base' => 'Tests\\ModuleTestCase',
This setting only affects generated test files. You can manually change the base class in existing tests.

Custom Stubs

The stubs setting allows you to customize the template files used when generating modules.

Default Behavior

'stubs' => null, // Uses package default stubs

Custom Stub Configuration

'stubs' => [
    'src/Providers/{{studly_name}}ServiceProvider.php' => base_path('stubs/app-modules/ServiceProvider.php'),
    'src/Models/.gitkeep' => base_path('stubs/app-modules/gitkeep.stub'),
],
The key is the destination path (relative to the module root), and the value is the absolute path to your custom stub file.

Available Placeholders

Stubs support these placeholders:
PlaceholderExampleDescription
{{studly_name}}BlogStudlyCase module name
{{snake_name}}blogsnake_case module name
{{namespace}}Modules\BlogFull module namespace
{{vendor}}acmeVendor name from config

Example Custom Stub

// stubs/app-modules/ServiceProvider.php
<?php

namespace {{namespace}}\Providers;

use Illuminate\Support\ServiceProvider;

class {{studly_name}}ServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Register {{studly_name}} services
    }
    
    public function boot(): void
    {
        // Bootstrap {{studly_name}} services
    }
}
Custom stubs are useful for organizations that want to standardize module structure across teams.

Event Discovery Configuration

The should_discover_events setting controls whether events and listeners are auto-discovered.

Default Behavior

'should_discover_events' => null, // Auto-detect from app config
When null, the package checks your App\Providers\EventServiceProvider to determine if event discovery is enabled. Source: src/Plugins/EventsPlugin.php:49-60
protected function shouldDiscoverEvents(): bool
{
    return $this->config->get('app-modules.should_discover_events') 
        ?? $this->appIsConfiguredToDiscoverEvents();
}

protected function appIsConfiguredToDiscoverEvents(): bool
{
    return collect($this->app->getProviders(EventServiceProvider::class))
        ->filter(fn(EventServiceProvider $provider) => 
            $provider::class === EventServiceProvider::class
            || str_starts_with(get_class($provider), $this->app->getNamespace())
        )
        ->contains(fn(EventServiceProvider $provider) => 
            $provider->shouldDiscoverEvents()
        );
}

Enable Event Discovery

'should_discover_events' => true,
Forces event discovery for modules, even if your app’s EventServiceProvider doesn’t have it enabled.

Disable Event Discovery

'should_discover_events' => false,
Completely disables event discovery for modules. You’ll need to manually register event listeners.
See Auto-Discovery for more details on how event discovery works.

Namespace Best Practices

1
Choose Your Organization Namespace
2
Use your organization’s name instead of the generic Modules:
3
'modules_namespace' => 'Acme',
4
Match Vendor to Namespace
5
Keep vendor name consistent with namespace:
6
'modules_namespace' => 'Acme',
'modules_vendor' => 'acme', // or null to auto-generate
7
Set Configuration Before Creating Modules
8
Configure namespaces before running php artisan make:module to avoid refactoring.
9
Document Your Conventions
10
If using custom namespaces or stubs, document them for your team.

Example: Enterprise Configuration

Here’s a complete example for an enterprise application:
// config/app-modules.php
return [
    // Organization namespace
    'modules_namespace' => 'AcmeEnterprise',
    
    // Composer vendor
    'modules_vendor' => 'acme-enterprise',
    
    // Keep default directory
    'modules_directory' => 'app-modules',
    
    // Custom base test case with enterprise setup
    'tests_base' => 'Tests\\EnterpriseTestCase',
    
    // Custom stubs with enterprise headers
    'stubs' => [
        'src/Providers/{{studly_name}}ServiceProvider.php' => 
            base_path('stubs/enterprise/ServiceProvider.php'),
        'README.md' => 
            base_path('stubs/enterprise/README.md'),
    ],
    
    // Enable event discovery
    'should_discover_events' => true,
];
This configuration creates modules like:
AcmeEnterprise\CustomerManagement\Models\Customer
AcmeEnterprise\OrderProcessing\Services\OrderService
AcmeEnterprise\Reporting\Reports\SalesReport
With this configuration, each module can be easily extracted to a standalone package with the name acme-enterprise/customer-management.

Changing Namespace After Creation

If you need to change the namespace after creating modules:
1
Update Configuration
2
'modules_namespace' => 'NewNamespace',
3
Regenerate Composer Files
4
php artisan modules:sync
5
Update Imports
6
Find and replace old namespace in all module files:
7
# Example using grep and sed (be careful!)
find app-modules -type f -name "*.php" -exec sed -i 's/OldNamespace/NewNamespace/g' {} +
8
Clear Caches
9
php artisan optimize:clear
composer dump-autoload
Changing namespaces after development has started can be error-prone. It’s highly recommended to set your namespace configuration before creating modules.

Build docs developers (and LLMs) love