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:
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:
Modules → modules
Acme → acme
AcmeCorp → acme-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.
Why app-modules/ is Recommended
The default app-modules/ directory has several advantages:
Alphabetical sorting : Appears before app/ in directory listings, making modules prominent
Clear separation : Distinguishes modular code from traditional Laravel app code
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:
Placeholder Example Description {{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.
Namespace Best Practices
Choose Your Organization Namespace
Use your organization’s name instead of the generic Modules:
'modules_namespace' => 'Acme' ,
Match Vendor to Namespace
Keep vendor name consistent with namespace:
'modules_namespace' => 'Acme' ,
'modules_vendor' => 'acme' , // or null to auto-generate
Set Configuration Before Creating Modules
Configure namespaces before running php artisan make:module to avoid refactoring.
Document Your Conventions
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:
'modules_namespace' => 'NewNamespace' ,
Regenerate Composer Files
Find and replace old namespace in all module files:
# Example using grep and sed (be careful!)
find app-modules -type f -name "*.php" -exec sed -i 's/OldNamespace/NewNamespace/g' {} +
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.