Module Lifecycle
AutoCom dispatches events at key points during module loading and tenant-level module management. These events allow you to hook into the module lifecycle for logging, monitoring, or custom behavior.
Lifecycle Overview
Application Boot
│
▼
┌──────────────────┐
│ ModuleLoading │ ◄── Event dispatched
└────────┬─────────┘
│
Load Module
│
▼
┌────┴────┐
│ │
Success Failure
│ │
▼ ▼
┌─────────┐ ┌──────────────┐
│ Loaded │ │ LoadFailed │ ◄── Events
└─────────┘ └──────────────┘
For tenant-level module management:
Tenant enables module Tenant disables module
│ │
▼ ▼
┌───────────────┐ ┌────────────────┐
│ ModuleEnabled │ │ ModuleDisabled │
└───────────────┘ └────────────────┘
Available Events
ModuleLoading
Dispatched before a module's service providers are registered.
namespace App\Core\Events;
class ModuleLoading
{
public function __construct(
public readonly string $moduleName,
public readonly array $config
) {}
}
Use cases:
- Pre-load logging
- Configuration validation
- Performance monitoring (start timer)
ModuleLoaded
Dispatched after a module successfully loads.
namespace App\Core\Events;
class ModuleLoaded
{
public function __construct(
public readonly string $moduleName,
public readonly array $config
) {}
}
Use cases:
- Success logging
- Performance monitoring (end timer)
- Cache warming
- Feature flag initialization
ModuleLoadFailed
Dispatched when a module fails to load.
namespace App\Core\Events;
class ModuleLoadFailed
{
public function __construct(
public readonly string $moduleName,
public readonly Throwable $exception
) {}
}
Use cases:
- Error logging and alerting
- Fallback behavior
- Admin notifications
- Health check reporting
ModuleEnabled
Dispatched when a tenant enables a module.
namespace App\Core\Events;
class ModuleEnabled
{
public function __construct(
public readonly string $moduleAlias,
public readonly ?string $tenantId = null,
public readonly array $settings = []
) {}
}
Use cases:
- Tenant activity logging
- Initial data seeding
- Welcome emails
- Analytics tracking
ModuleDisabled
Dispatched when a tenant disables a module.
namespace App\Core\Events;
class ModuleDisabled
{
public function __construct(
public readonly string $moduleAlias,
public readonly ?string $tenantId = null
) {}
}
Use cases:
- Cleanup tasks
- Data export reminders
- Analytics tracking
- Resource deallocation
Listening to Events
Creating a Listener
namespace App\Listeners;
use App\Core\Events\ModuleLoaded;
use Illuminate\Support\Facades\Log;
class LogModuleLoaded
{
public function handle(ModuleLoaded $event): void
{
Log::info("Module loaded: {$event->moduleName}", [
'version' => $event->config['version'] ?? 'unknown',
'type' => $event->config['type'] ?? 'standard',
]);
}
}
Registering Listeners
In your EventServiceProvider:
namespace App\Providers;
use App\Core\Events\ModuleLoading;
use App\Core\Events\ModuleLoaded;
use App\Core\Events\ModuleLoadFailed;
use App\Core\Events\ModuleEnabled;
use App\Core\Events\ModuleDisabled;
use App\Listeners\LogModuleLoaded;
use App\Listeners\HandleModuleFailure;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
ModuleLoaded::class => [
LogModuleLoaded::class,
],
ModuleLoadFailed::class => [
HandleModuleFailure::class,
],
ModuleEnabled::class => [
// Your listeners
],
ModuleDisabled::class => [
// Your listeners
],
];
}
Using Closures
For quick listeners in a service provider:
use App\Core\Events\ModuleLoaded;
use Illuminate\Support\Facades\Event;
public function boot(): void
{
Event::listen(ModuleLoaded::class, function (ModuleLoaded $event) {
// Handle the event
});
}
Practical Examples
Performance Monitoring
Track module load times:
namespace App\Listeners;
use App\Core\Events\ModuleLoading;
use App\Core\Events\ModuleLoaded;
use Illuminate\Support\Facades\Cache;
class ModulePerformanceMonitor
{
public function handleLoading(ModuleLoading $event): void
{
Cache::put("module_load_start_{$event->moduleName}", microtime(true), 60);
}
public function handleLoaded(ModuleLoaded $event): void
{
$start = Cache::pull("module_load_start_{$event->moduleName}");
if ($start) {
$duration = (microtime(true) - $start) * 1000;
Log::debug("Module {$event->moduleName} loaded in {$duration}ms");
// Report to monitoring service
if ($duration > 100) {
Log::warning("Slow module load: {$event->moduleName} took {$duration}ms");
}
}
}
}
Register both handlers:
protected $listen = [
ModuleLoading::class => [
[ModulePerformanceMonitor::class, 'handleLoading'],
],
ModuleLoaded::class => [
[ModulePerformanceMonitor::class, 'handleLoaded'],
],
];
Error Alerting
Send alerts when modules fail:
namespace App\Listeners;
use App\Core\Events\ModuleLoadFailed;
use App\Notifications\ModuleFailedNotification;
use Illuminate\Support\Facades\Notification;
class AlertOnModuleFailure
{
public function handle(ModuleLoadFailed $event): void
{
// Log the error
Log::error("Module failed to load: {$event->moduleName}", [
'error' => $event->exception->getMessage(),
'trace' => $event->exception->getTraceAsString(),
]);
// Alert admins for core modules
$config = $this->getModuleConfig($event->moduleName);
if ($config['isCore'] ?? false) {
Notification::route('slack', config('services.slack.webhook'))
->notify(new ModuleFailedNotification($event->moduleName, $event->exception));
}
}
}
Tenant Module Analytics
Track module usage across tenants:
namespace App\Listeners;
use App\Core\Events\ModuleEnabled;
use App\Core\Events\ModuleDisabled;
use App\Models\ModuleAnalytics;
class TrackModuleUsage
{
public function handleEnabled(ModuleEnabled $event): void
{
ModuleAnalytics::create([
'module_alias' => $event->moduleAlias,
'tenant_id' => $event->tenantId,
'action' => 'enabled',
'settings' => $event->settings,
'created_at' => now(),
]);
}
public function handleDisabled(ModuleDisabled $event): void
{
ModuleAnalytics::create([
'module_alias' => $event->moduleAlias,
'tenant_id' => $event->tenantId,
'action' => 'disabled',
'created_at' => now(),
]);
}
}
Module-Specific Initialization
Run setup when a module is enabled for a tenant:
namespace Modules\StoreShopify\Listeners;
use App\Core\Events\ModuleEnabled;
use Modules\StoreShopify\Services\ShopifyService;
class InitializeShopifyForTenant
{
public function handle(ModuleEnabled $event): void
{
if ($event->moduleAlias !== 'store-shopify') {
return;
}
// Run Shopify-specific initialization
app(ShopifyService::class)->initializeForTenant(
$event->tenantId,
$event->settings
);
}
}
Module Service Provider Hooks
In addition to events, modules can implement lifecycle methods in their service providers:
Register Phase
Called when the module is being registered (before boot):
class MyModuleServiceProvider extends ServiceProvider
{
public function register(): void
{
// Merge config
$this->mergeConfigFrom(__DIR__ . '/../module.json', 'my-module');
// Register services (not available yet to other modules)
$this->app->singleton(MyService::class, fn() => new MyService());
}
}
Boot Phase
Called after all modules are registered:
class MyModuleServiceProvider extends ServiceProvider
{
public function boot(): void
{
// Load routes (after all services are available)
$this->loadRoutes();
// Load migrations
$this->loadMigrationsFrom(__DIR__ . '/database/migrations');
// Register with other modules
$this->registerWidgets();
$this->registerWorkflowNodes();
}
}
Conditional Registration
Check if other modules are available:
public function boot(): void
{
// Register widgets only if CoreDashboard is loaded
if (class_exists(\Modules\CoreDashboard\Registries\WidgetRegistry::class)) {
$this->registerWidgets();
}
// Register workflow nodes only if Workflows is loaded
if (class_exists(\Modules\Workflows\Registries\WorkflowNodeRegistry::class)) {
$this->registerWorkflowNodes();
}
}
Debugging Lifecycle
Enable Debug Logging
Add to your .env:
LOG_LEVEL=debug
Then check logs:
tail -f storage/logs/laravel.log | grep ModuleLoader
Output:
[2024-01-20 10:00:00] local.DEBUG: ModuleLoader: Loading module 'Core'
[2024-01-20 10:00:00] local.DEBUG: ModuleLoader: Registered provider 'Modules\Core\CoreServiceProvider'
[2024-01-20 10:00:00] local.INFO: ModuleLoader: Successfully loaded module 'Core'
[2024-01-20 10:00:01] local.INFO: ModuleLoader: Loading complete {"loaded":5,"errors":0}
Check Load Order
php artisan module:list --graph
Next Steps
- Learn about Module Dependencies
- Review CLI Commands for management
- See Module Structure for configuration