Module discovery, registration, lifecycle management, and dependency resolution. The Module System provides a powerful plugin architecture that allows you to extend the framework's functionality through modular components.
PHP 8 attribute that marks a class as a Forge Engine module. Modules use attributes for configuration rather than extending a base class, providing a clean and modern approach to module definition.
Create a custom module using PHP 8 attributes to mark the class as a Forge Engine module.
<?php
declare(strict_types=1);
namespace App\Modules\Blog;
use Forge\Core\DI\Container;
use Forge\Core\Module\Attributes\Module;
use Forge\Core\Module\Attributes\LifecycleHook;
use Forge\Core\Module\Attributes\Compatibility;
use Forge\Core\Module\Attributes\Provides;
use Forge\Core\Module\Attributes\Requires;
use Forge\Core\Module\Attributes\Repository;
use Forge\Core\Module\LifecycleHookName;
#[
    Module(
        name: "Blog",
        description: "Blog functionality for the application",
        version: "1.0.0",
        order: 100,
        core: false,
        isCli: false
    ),
    Compatibility(framework: ">=0.1.0", php: ">=8.3"),
    Repository(type: "git", url: "https://github.com/forge-engine/modules"),
    Provides(interface: BlogServiceInterface::class, version: "1.0.0")
]
#[Requires]
final class BlogModule
{
    public function register(Container $container): void
    {
        // Register services early in the application lifecycle
        $container->bind(BlogService::class, BlogService::class);
        $container->bind(PostRepository::class, PostRepository::class);
    }
    
    #[LifecycleHook(hook: LifecycleHookName::AFTER_MODULE_REGISTER)]
    public function onAfterModuleRegister(): void
    {
        // Module initialization logic after registration
    }
}Properties available in the Module attribute constructor.
#[Module(
    name: "Blog",                    // Module identifier (required)
    version: "1.0.0",                // Semantic version (optional)
    description: "Blog functionality", // Human-readable description (optional)
    order: 100,                      // Loading order, higher loads first (optional, default: PHP_INT_MAX)
    core: false,                     // Whether this is a core module (optional, default: false)
    isCli: false                     // Whether module provides CLI commands (optional, default: false)
)]Other attributes used to configure modules.
#[Compatibility(framework: ">=0.1.0", php: ">=8.3")]
#[Repository(type: "git", url: "https://github.com/forge-engine/modules")]
#[Provides(interface: BlogServiceInterface::class, version: "1.0.0")]
#[Requires] // Can specify required modules or interfacesRegister services early in the application lifecycle. This method is called for all modules before any lifecycle hooks.
public function register(Container $container): void
{
    // Register services that other modules might depend on
    $container->bind(BlogService::class, BlogService::class);
    
    // Register configuration
    $container->bind('blog.config', function() {
        return new Config([
            'posts_per_page' => 10,
            'allow_comments' => true,
            'moderation_required' => true,
            'seo_enabled' => true
        ]);
    });
    
    // Register repositories
    $container->bind(PostRepository::class, PostRepository::class);
}Use attributes to mark methods that should be called at specific points in the application lifecycle.
use Forge\Core\Module\Attributes\LifecycleHook;
use Forge\Core\Module\LifecycleHookName;
#[LifecycleHook(hook: LifecycleHookName::AFTER_MODULE_REGISTER)]
public function onAfterModuleRegister(): void
{
    // Called after all modules have been registered
    // Safe to interact with other modules here
}
#[LifecycleHook(hook: LifecycleHookName::AFTER_BOOT)]
public function onAfterBoot(): void
{
    // Called after the application has booted
    // All services are available
}
#[LifecycleHook(hook: LifecycleHookName::BEFORE_REQUEST)]
public function onBeforeRequest(): void
{
    // Called before each request is processed
    // Useful for request-specific initialization
}
#[LifecycleHook(hook: LifecycleHookName::AFTER_REQUEST)]
public function onAfterRequest(): void
{
    // Called after each request is processed
    // Useful for cleanup or logging
}All available lifecycle hook names from the LifecycleHookName enum.
enum LifecycleHookName: string
{
    case AFTER_BOOT = 'afterBoot';                    // After application boot
    case AFTER_MODULE_LOAD = 'afterModuleLoad';      // After modules are loaded
    case AFTER_MODULE_REGISTER = 'afterModuleRegister'; // After all modules registered
    case AFTER_CONFIG_LOADED = 'afterConfigLoaded';  // After configuration loaded
    case APP_BOOTED = 'appBooted';                    // After application fully booted
    case BEFORE_REQUEST = 'beforeRequest';          // Before each request
    case AFTER_REQUEST = 'afterRequest';             // After each request
    case AFTER_RESPONSE = 'afterResponse';           // After response sent
}Complete reference of all available PHP 8 attributes for defining and configuring modules in Forge Engine. These attributes provide a declarative way to configure module metadata, dependencies, compatibility, and behavior.
Required attribute that marks a class as a Forge Engine module and defines its basic properties.
use Forge\Core\Module\Attributes\Module;
#[Module(
    name: "Blog",                    // Required: Module identifier
    version: "1.0.0",                // Optional: Semantic version
    description: "Blog functionality", // Optional: Human-readable description
    order: 100,                      // Optional: Loading order (higher loads first)
    core: false,                     // Optional: Whether this is a core module
    isCli: false                     // Optional: Whether module provides CLI commands
)]
final class BlogModule
{
    // Module implementation
}Defines compatibility requirements for Forge Engine framework and PHP versions.
use Forge\Core\Module\Attributes\Compatibility;
#[Compatibility(
    framework: ">=0.1.0",            // Forge Engine version constraint
    php: ">=8.3"                    // PHP version constraint
)]
final class BlogModule
{
    // Module implementation
}Specifies the source code repository for the module, used by the PackageManager.
use Forge\Core\Module\Attributes\Repository;
#[Repository(
    type: "git",                     // Repository type (git, svn, etc.)
    url: "https://github.com/forge-engine/forge-blog.git" // Repository URL
)]
final class BlogModule
{
    // Module implementation
}Declares that this module provides implementations for specific interfaces. Can be repeated multiple times.
use Forge\Core\Module\Attributes\Provides;
#[Provides(interface: BlogServiceInterface::class, version: "1.0.0")]
#[Provides(interface: PostRepositoryInterface::class, version: "1.0.0")]
#[Provides(interface: CommentServiceInterface::class, version: "1.0.0")]
final class BlogModule
{
    // Module provides implementations for these interfaces
}Declares dependencies on other modules or interfaces. Can be used on the class or specific methods.
use Forge\Core\Module\Attributes\Requires;
#[Requires] // Class-level dependency (module requires other modules)
final class BlogModule
{
    #[Requires] // Method-level dependency
    public function register(Container $container): void
    {
        // This method requires other modules to be available
    }
}Defines default configuration values for the module. These values are merged with user configuration.
use Forge\Core\Module\Attributes\ConfigDefaults;
#[ConfigDefaults([
    'posts_per_page' => 10,
    'allow_comments' => true,
    'moderation_required' => true,
    'seo_enabled' => true,
    'cache_ttl' => 3600
])]
final class BlogModule
{
    // Module with default configuration
}Example from ForgePackageManager module:
#[ConfigDefaults([
    'repositories' => [
        'packagist' => [
            'type' => 'composer',
            'url' => 'https://repo.packagist.org'
        ]
    ],
    'cache_ttl' => 3600,
    'auto_update' => false
])]
final class ForgePackageManager
{
    // Package manager with default repository configuration
}Defines a CLI command that this module provides. Used to register commands with the CLI system.
use Forge\Core\Module\Attributes\CLICommand;
#[CLICommand(
    name: "blog:install",            // Command name
    description: "Install the blog module" // Command description
)]
final class InstallCommand
{
    // CLI command implementation
}Defines navigation items for the admin hub interface. Can be repeated multiple times for multiple menu items.
use Forge\Core\Module\Attributes\HubItem;
use Forge\Core\Module\ForgeIcon;
#[HubItem(
    label: "Blog Posts",             // Menu item label
    route: "admin.blog.posts",       // Route name
    icon: ForgeIcon::POSTS,          // Icon constant
    order: 10,                       // Menu order (lower appears first)
    permissions: ["blog.manage"]       // Required permissions
)]
#[HubItem(
    label: "Categories",
    route: "admin.blog.categories",
    icon: ForgeIcon::CATEGORIES,
    order: 20,
    permissions: ["blog.manage"]
)]
final class BlogModule
{
    // Module adds navigation items to admin hub
}Marks a method to be called at specific points in the application lifecycle.
use Forge\Core\Module\Attributes\LifecycleHook;
use Forge\Core\Module\LifecycleHookName;
#[LifecycleHook(hook: LifecycleHookName::AFTER_MODULE_REGISTER)]
public function onAfterModuleRegister(): void
{
    // Called after all modules have been registered
}
#[LifecycleHook(hook: LifecycleHookName::AFTER_BOOT)]
public function onAfterBoot(): void
{
    // Called after the application has fully booted
}
#[LifecycleHook(hook: LifecycleHookName::BEFORE_REQUEST)]
public function onBeforeRequest(): void
{
    // Called before each HTTP request
}Central manager for module discovery, loading, and lifecycle management. The ModuleManager handles dependency resolution, module ordering, and provides methods for enabling, disabling, and updating modules.
Forge Engine uses a ModuleLoader to discover, load, and manage modules throughout the application lifecycle. Modules are loaded based on their forge.json configuration files.
Modules are automatically discovered by scanning the /modules directory for forge.json files.
// The ModuleLoader automatically discovers modules in the modules directory
$loader = new ModuleLoader($this->container);
// Modules are discovered based on forge.json files
// Example forge.json structure:
{
    "$schema": "engine/Core/Schema/module-schema.json",
    "name": "ForgeBlog",
    "version": "1.0.0",
    "description": "Blog functionality for Forge Engine",
    "type": "module",
    "order": 10,
    "provides": ["blog", "posts"],
    "requires": {
        "ForgeAuth": "^1.0.0",
        "ForgeUI": "^1.0.0"
    },
    "lifecycleHooks": {
        "register": "register",
        "afterModuleRegister": "onAfterModuleRegister"
    },
    "class": "Modules\\ForgeBlog\\ForgeBlogModule",
    "cli": {
        "commands": {
            "blog:install": "Modules\\ForgeBlog\\Commands\\InstallCommand"
        }
    },
    "compatibility": {
        "engine": "^3.0.0"
    },
    "repository": {
        "type": "git",
        "url": "https://github.com/forge-engine/forge-blog.git"
    }
}The ModuleLoader follows a specific process to load and register modules.
// In ModuleSetup.php - the bootstrap process
public function loadModules(): void
{
    $loader = new ModuleLoader($this->container);
    
    // 1. Discover all modules by scanning forge.json files
    $modules = $loader->discoverModules();
    
    // 2. Sort modules based on order and dependencies
    $sortedModules = $loader->sortModulesByDependencies($modules);
    
    // 3. Load each module class
    foreach ($sortedModules as $moduleConfig) {
        $moduleClass = $moduleConfig['class'];
        $module = new $moduleClass();
        
        // 4. Call register() method on each module
        $module->register($this->container);
        
        // 5. Store module instance for later use
        $this->modules[$moduleConfig['name']] = $module;
    }
    
    // 6. Trigger AFTER_MODULE_LOAD lifecycle hook
    $this->eventDispatcher->dispatch(LifecycleHookName::AFTER_MODULE_LOAD);
    
    // 7. Call lifecycle hook methods on each module
    foreach ($this->modules as $module) {
        $this->callLifecycleHooks($module);
    }
}Modules are loaded in the correct order based on their dependencies.
// Example dependency resolution
$modules = [
    'ForgeAuth' => ['order' => 1, 'provides' => ['auth']],
    'ForgeUI' => ['order' => 2, 'provides' => ['ui', 'templates']],
    'ForgeBlog' => ['order' => 10, 'requires' => ['auth', 'ui']]
];
// The loader will automatically:
// 1. Load ForgeAuth first (order 1, no dependencies)
// 2. Load ForgeUI second (order 2, no dependencies)
// 3. Load ForgeBlog last (order 10, depends on auth and ui)
// If circular dependencies are detected, an exception is thrown
// If required modules are missing, an exception is thrownModule management in Forge Engine is handled through the PackageManager module and CLI commands, not through a programmatic API.
Use the Forge CLI to manage modules from the command line.
# List all available modules
php forge module:list
# Install a module
php forge module:install ForgeBlog
# Uninstall a module
php forge module:uninstall ForgeBlog
# Enable a module
php forge module:enable ForgeBlog
# Disable a module
php forge module:disable ForgeBlog
# Update a module
php forge module:update ForgeBlog
# Check module status
php forge module:status ForgeBlogFor programmatic module management, use the PackageManager service.
use Forge\Core\PackageManager\Services\PackageManagerService;
$packageManager = $container->make(PackageManagerService::class);
// Get all installed modules
$modules = $packageManager->getInstalledModules();
// Install a module from repository
$result = $packageManager->installModule('ForgeBlog', '1.0.0');
// Uninstall a module
$result = $packageManager->uninstallModule('ForgeBlog');
// Update a module
$result = $packageManager->updateModule('ForgeBlog', '2.0.0');
// Check if module is installed
$isInstalled = $packageManager->isModuleInstalled('ForgeBlog');
// Get module information
$moduleInfo = $packageManager->getModuleInfo('ForgeBlog');Module configuration is managed through the forge.json file and can be accessed programmatically.
// Access module configuration from within a module
public function register(Container $container): void
{
    // Get the current module's configuration
    $moduleConfig = $this->getModuleConfig();
    
    // Access specific configuration values
    $postsPerPage = $moduleConfig['posts_per_page'] ?? 10;
    $allowComments = $moduleConfig['allow_comments'] ?? true;
    
    // Register configuration as a service
    $container->bind('blog.config', function() use ($moduleConfig) {
        return new Config($moduleConfig);
    });
}
// Update module configuration programmatically
$packageManager = $container->make(PackageManagerService::class);
$packageManager->updateModuleConfig('ForgeBlog', [
    'posts_per_page' => 20,
    'allow_comments' => false
]);Get information about loaded modules from the container or PackageManager.
use Forge\Core\PackageManager\Services\PackageManagerService;
$packageManager = $container->make(PackageManagerService::class);
// Get information about a specific module
$moduleInfo = $packageManager->getModuleInfo('ForgeBlog');
if ($moduleInfo) {
    echo "Module: " . $moduleInfo['name'] . "\n";
    echo "Version: " . $moduleInfo['version'] . "\n";
    echo "Description: " . $moduleInfo['description'] . "\n";
    echo "Type: " . $moduleInfo['type'] . "\n";
    echo "Order: " . $moduleInfo['order'] . "\n";
    echo "Provides: " . implode(', ', $moduleInfo['provides'] ?? []) . "\n";
    echo "Requires: " . json_encode($moduleInfo['requires'] ?? []) . "\n";
    echo "Author: " . ($moduleInfo['author'] ?? 'Unknown') . "\n";
    echo "License: " . ($moduleInfo['license'] ?? 'Unknown') . "\n";
}
// Get all installed modules
$allModules = $packageManager->getInstalledModules();
foreach ($allModules as $moduleName => $moduleData) {
    echo "Module: {$moduleName}\n";
    echo "  Version: {$moduleData['version']}\n";
    echo "  Description: {$moduleData['description']}\n";
    echo "  Status: " . ($moduleData['enabled'] ? 'Enabled' : 'Disabled') . "\n";
}
    echo "\n";
}Get the status of a module.
$status = $manager->getStatus('blog');
switch ($status) {
    case 'installed':
        echo "Module is installed and ready";
        break;
    case 'enabled':
        echo "Module is enabled and active";
        break;
    case 'disabled':
        echo "Module is disabled";
        break;
    case 'not_installed':
        echo "Module is not installed";
        break;
    case 'error':
        echo "Module has errors";
        break;
}
                                Forge Engine uses a sophisticated module loading system that discovers modules through their forge.json configuration files and loads them using PHP 8 attributes. The system handles dependency resolution, lifecycle hooks, and module validation automatically.
                            
Modules are automatically discovered by scanning the /modules directory for forge.json files.
// The ModuleLoader automatically discovers modules during bootstrap
// No manual discovery needed - just place your module in the /modules directory
// Example module structure:
/modules/
├── ForgeAuth/
│   ├── forge.json          # Module configuration
│   ├── src/
│   │   └── ForgeAuthModule.php  # Module class
│   └── ...
├── ForgeBlog/
│   ├── forge.json
│   ├── src/
│   │   └── ForgeBlogModule.php
│   └── ...
└── ForgeUI/
    ├── forge.json
    ├── src/
    │   └── ForgeUIModule.php
    └── ...Modules are validated against the JSON schema during discovery.
// The ModuleLoader validates each forge.json against the schema
// engine/Core/Schema/module-schema.json
// Validation checks include:
// - Required fields (name, version, class)
// - Version format compliance
// - Dependency format
// - Lifecycle hook method existence
// - Class file accessibility
// If validation fails, the module is skipped and an error is loggedModules are loaded in the correct order based on their dependencies and order values.
// Example dependency resolution process:
$modules = [
    'ForgeAuth' => ['order' => 1, 'provides' => ['auth']],
    'ForgeUI' => ['order' => 2, 'provides' => ['ui', 'templates']],
    'ForgeBlog' => ['order' => 10, 'requires' => ['auth', 'ui']]
];
// The loader will:
// 1. Sort by order field (lower numbers first)
// 2. Resolve dependencies (required modules loaded first)
// 3. Detect circular dependencies
// 4. Throw exception if required modules are missing
// 5. Load modules in resolved orderCommon patterns and examples for working with the Module System.
// Example ForgeBlogModule.php
namespace Modules\ForgeBlog;
use Forge\Core\Module\Attributes\Module;
use Forge\Core\Module\Attributes\Compatibility;
use Forge\Core\Module\Attributes\Repository;
use Forge\Core\Module\Attributes\Provides;
use Forge\Core\Module\Attributes\Requires;
use Forge\Core\Module\Attributes\LifecycleHook;
use Forge\Core\Module\LifecycleHookName;
#[Module(
    name: "ForgeBlog",
    version: "1.0.0",
    description: "Blog functionality for Forge Engine",
    order: 10,
    core: false,
    isCli: false
)]
#[Compatibility(engine: "^3.0.0")]
#[Repository(type: "git", url: "https://github.com/forge-engine/forge-blog.git")]
#[Provides("blog")]
#[Provides("posts")]
#[Requires("ForgeAuth", "^1.0.0")]
#[Requires("ForgeUI", "^1.0.0")]
class ForgeBlogModule
{
    public function register(Container $container): void
    {
        // Register blog services
        $container->bind(BlogService::class, BlogService::class);
        $container->bind(PostRepository::class, PostRepository::class);
        
        // Register configuration
        $container->bind('blog.config', function() {
            return new Config([
                'posts_per_page' => 10,
                'allow_comments' => true,
                'moderation_required' => true,
                'seo_enabled' => true
            ]);
        });
    }
    #[LifecycleHook(hook: LifecycleHookName::AFTER_MODULE_REGISTER)]
    public function onAfterModuleRegister(): void
    {
        // Register routes, event listeners, etc.
        // This is called after all modules are registered
        $this->registerRoutes();
        $this->registerEventListeners();
    }
    private function registerRoutes(): void
    {
        // Register blog routes
        $router = Container::getInstance()->make('router');
        $router->get('/blog', [BlogController::class, 'index']);
        $router->get('/blog/{slug}', [BlogController::class, 'show']);
    }
    private function registerEventListeners(): void
    {
        $events = Container::getInstance()->make('events');
        $events->listen('blog.post.created', function($post) {
            // Handle post creation
        });
    }
}// forge.json - Module configuration file
{
    "name": "ForgeBlog",
    "version": "1.0.0",
    "description": "Blog functionality for Forge Engine",
    "type": "module",
    "keywords": ["blog", "posts", "content"],
    "homepage": "https://github.com/forge-engine/forge-blog",
    "license": "MIT",
    "authors": [
        {
            "name": "Forge Engine Team",
            "email": "team@forge-engine.com"
        }
    ],
    "support": {
        "issues": "https://github.com/forge-engine/forge-blog/issues",
        "source": "https://github.com/forge-engine/forge-blog"
    },
    "require": {
        "php": "^8.1",
        "forge-engine/core": "^3.0.0",
        "forge-engine/forge-auth": "^1.0.0",
        "forge-engine/forge-ui": "^1.0.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^10.0"
    },
    "autoload": {
        "psr-4": {
            "Modules\\ForgeBlog\\": "src/"
        }
    },
    "extra": {
        "forge": {
            "class": "Modules\\ForgeBlog\\ForgeBlogModule",
            "order": 10,
            "core": false,
            "isCli": false,
            "provides": ["blog", "posts"],
            "compatibility": {
                "engine": "^3.0.0"
            }
        }
    }
}// ForgeBlogModule.php - Using attributes for dependencies
use Forge\Core\Module\Attributes\Requires;
use Forge\Core\Module\Attributes\Provides;
#[Module(
    name: "ForgeBlog",
    version: "1.0.0",
    description: "Blog functionality for Forge Engine"
)]
#[Requires("ForgeAuth", "^1.0.0")]      // Requires auth module
#[Requires("ForgeUI", "^1.0.0")]        // Requires UI module
#[Requires("ForgeDatabase", "^1.0.0")]    // Requires database module
#[Provides("blog")]                        // Provides blog functionality
#[Provides("posts")]                      // Provides posts functionality
class ForgeBlogModule
{
    public function register(Container $container): void
    {
        // Services are automatically available through dependency injection
        $authService = $container->make('ForgeAuth');
        $uiService = $container->make('ForgeUI');
        $dbService = $container->make('ForgeDatabase');
        
        // Register blog services with dependencies
        $container->bind(BlogService::class, function($container) use ($authService, $dbService) {
            return new BlogService(
                $container->make(PostRepository::class),
                $authService,
                $dbService
            );
        });
    }
    #[LifecycleHook(hook: LifecycleHookName::AFTER_MODULE_REGISTER)]
    public function onAfterModuleRegister(): void
    {
        // Dependencies are guaranteed to be loaded at this point
        $this->registerBlogRoutes();
        $this->setupBlogDatabase();
    }
    private function registerBlogRoutes(): void
    {
        // Use services from required modules via dependency injection
        $auth = Container::getInstance()->make('ForgeAuth');
        $router = Container::getInstance()->make('router');
        
        // Register protected blog routes
        $router->get('/blog', [BlogController::class, 'index']);
        $router->get('/blog/{slug}', [BlogController::class, 'show']);
        
        // Admin routes (require auth)
        $router->group(['middleware' => $auth->getAuthMiddleware()], function($router) {
            $router->get('/admin/blog', [AdminBlogController::class, 'index']);
            $router->post('/admin/blog/posts', [AdminBlogController::class, 'create']);
        });
    }
    private function setupBlogDatabase(): void
    {
        $db = Container::getInstance()->make('ForgeDatabase');
        
        // Create blog tables using the database service
        $db->createTable('blog_posts', function($table) {
            $table->id();
            $table->string('title');
            $table->string('slug')->unique();
            $table->text('content');
            $table->foreignId('author_id')->constrained('users');
            $table->timestamps();
        });
    }
}// ForgeBlogModule.php - Using lifecycle hooks and events
use Forge\Core\Module\Attributes\LifecycleHook;
use Forge\Core\Module\LifecycleHookName;
#[Module(name: "ForgeBlog", version: "1.0.0")]
class ForgeBlogModule
{
    #[LifecycleHook(hook: LifecycleHookName::AFTER_BOOT)]
    public function onAfterBoot(): void
    {
        // Register module-level event listeners
        $events = Container::getInstance()->make('events');
        
        // Listen for module system events
        $events->listen('module.installed', function($moduleName) {
            if ($moduleName === 'ForgeBlog') {
                $this->onBlogModuleInstalled();
            }
        });
        
        $events->listen('module.enabled', function($moduleName) {
            if ($moduleName === 'ForgeBlog') {
                $this->onBlogModuleEnabled();
            }
        });
        
        $events->listen('module.disabled', function($moduleName) {
            if ($moduleName === 'ForgeBlog') {
                $this->onBlogModuleDisabled();
            }
        });
    }
    #[LifecycleHook(hook: LifecycleHookName::AFTER_MODULE_REGISTER)]
    public function onAfterModuleRegister(): void
    {
        // Register blog-specific event listeners
        $events = Container::getInstance()->make('events');
        
        // Blog post events
        $events->listen('blog.post.created', function($post) {
            $this->clearPostCache($post->id);
            $this->notifySubscribers($post);
            $this->updateSearchIndex($post);
        });
        
        $events->listen('blog.post.updated', function($post) {
            $this->clearRelatedCaches($post);
            $this->updateSearchIndex($post);
        });
        
        $events->listen('blog.post.deleted', function($post) {
            $this->removeFromSearchIndex($post);
            $this->cleanupMedia($post);
            $this->clearPostCache($post->id);
        });
        
        // Category events
        $events->listen('blog.category.created', function($category) {
            $this->clearCategoryCache($category->id);
        });
        
        // Comment events
        $events->listen('blog.comment.created', function($comment) {
            $this->notifyPostAuthor($comment);
            $this->moderateComment($comment);
        });
    }
    private function onBlogModuleInstalled(): void
    {
        // Create default blog settings
        $config = Container::getInstance()->make('config');
        $config->set('blog.settings', [
            'posts_per_page' => 10,
            'allow_comments' => true,
            'moderation_required' => true
        ]);
        
        // Log installation
        Container::getInstance()->make('log')->info('ForgeBlog module installed successfully');
    }
    private function onBlogModuleEnabled(): void
    {
        // Clear blog-related caches
        Container::getInstance()->make('cache')->forget('blog.posts');
        Container::getInstance()->make('cache')->forget('blog.categories');
        
        // Rebuild search index
        $this->rebuildSearchIndex();
        
        // Log enablement
        Container::getInstance()->make('log')->info('ForgeBlog module enabled');
    }
    private function onBlogModuleDisabled(): void
    {
        // Clear all blog caches
        $this->clearAllBlogCaches();
        
        // Log disablement
        Container::getInstance()->make('log')->info('ForgeBlog module disabled');
    }
    // Helper methods for event handling
    private function clearPostCache($postId): void
    {
        Container::getInstance()->make('cache')->forget("blog.post.{$postId}");
        Container::getInstance()->make('cache')->forget("blog.post.{$postId}.html");
    }
    private function notifySubscribers($post): void
    {
        $notificationService = Container::getInstance()->make('notification');
        $subscribers = $this->getPostSubscribers($post->id);
        
        foreach ($subscribers as $subscriber) {
            $notificationService->send($subscriber, 'New blog post: ' . $post->title);
        }
    }
    private function updateSearchIndex($post): void
    {
        $searchService = Container::getInstance()->make('search');
        $searchService->index('blog_posts', $post->id, [
            'title' => $post->title,
            'content' => $post->content,
            'tags' => $post->tags,
            'category' => $post->category->name
        ]);
    }
}Recommended patterns and guidelines for working with the Module System.