install
This commit is contained in:
246
.ai/guidelines/modular-architecture.blade.php
Normal file
246
.ai/guidelines/modular-architecture.blade.php
Normal file
@@ -0,0 +1,246 @@
|
||||
### Modular Architecture Guidelines
|
||||
|
||||
#### Overview
|
||||
|
||||
This application uses a custom modular architecture to organize features into distinct, self-contained units called "Modules". Each module encapsulates a specific piece of functionality, including its own models, migrations, seeders, controllers, and more. The system is designed to automatically discover and register components from enabled modules.
|
||||
|
||||
The core of this system is the `App\Modules\ModuleRepository`, which is responsible for finding, loading, and managing all the modules in the application. A set of helper functions is provided for easy interaction with the module repository.
|
||||
|
||||
#### Core Concepts
|
||||
|
||||
- **Module**: A directory within `app/Modules` that represents a distinct feature.
|
||||
- **`ModuleRepository`**: A singleton service (`App\Modules\ModuleRepository`) that manages all modules. Accessed via the `modular()` helper.
|
||||
- **`ModuleServiceProvider`**: A service provider (`App\Modules\ModuleServiceProvider`) that automatically discovers and registers resources (routes, migrations, views, etc.) from all enabled modules.
|
||||
- **`ModuleContract`**: An interface (`App\Modules\ModuleContract`) that every module's main class must implement. It defines the basic contract for a module.
|
||||
- **`BaseModule`**: A wrapper class (`App\Modules\BaseModule`) that holds information about a module, such as its path, name, and an instance of its `ModuleContract`.
|
||||
- **Helpers**: Global functions defined in `app/Helpers/helpers.php` to simplify interaction with the modular system.
|
||||
- **Core Module**: A special module located in `app/Modules/Core` that provides Artisan commands for creating new modules and their components.
|
||||
|
||||
#### Module Structure
|
||||
|
||||
Every module is a directory located in `app/Modules/`. For a module named `Example`, the structure would be `app/Modules/Example/`.
|
||||
|
||||
##### Required Structure
|
||||
|
||||
- `app/Modules/Example/ExampleModule.php`: This is the main class for the module. It **must** implement `App\Modules\ModuleContract`.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace App\Modules\Example;
|
||||
|
||||
use App\Modules\ModuleContract;
|
||||
|
||||
class ExampleModule implements ModuleContract
|
||||
{
|
||||
/**
|
||||
* Check if module is enabled
|
||||
*/
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable module
|
||||
*/
|
||||
public function disable(): void
|
||||
{
|
||||
// Logic to disable the module
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable module
|
||||
*/
|
||||
public function enable(): void
|
||||
{
|
||||
// Logic to enable the module
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if module has a filament resource
|
||||
*/
|
||||
public function hasFilamentResource(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get module composer requirements
|
||||
*
|
||||
* @return array<int, \App\Modules\Core\ModulePackage>
|
||||
*/
|
||||
public function getComposerRequirements(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get module composer suggestions
|
||||
*
|
||||
* @return array<int, \App\Modules\Core\ModulePackage>
|
||||
*/
|
||||
public function getComposerSuggestions(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### Automatic Discovery
|
||||
|
||||
The `ModuleServiceProvider` automatically discovers and registers the following directories and files for all **enabled** modules. The paths shown are for an `Example` module. If module name is `MyModule`, kebab naming will be used for routes, helpers, and config files will be `app/Modules/MyModule/Configs/my-module-config.php`, `app/Modules/MyModule/Routes/my-module-routes.php`, `app/Modules/MyModule/my-module-helpers.php`.
|
||||
|
||||
- **Configurations**: `app/Modules/Example/Configs/example-config.php`
|
||||
- **Migrations**: `app/Modules/Example/Database/Migrations/`
|
||||
- **Seeders**: `app/Modules/Example/Database/Seeders/`
|
||||
- **Routes**: `app/Modules/Example/Routes/example-routes.php`
|
||||
- **Views**: `app/Modules/Example/Resources/Views/`
|
||||
- **Translations**: `app/Modules/Example/Resources/lang/`
|
||||
- **Helper Files**: `app/Modules/Example/example-helpers.php`
|
||||
|
||||
##### Composer Dependencies
|
||||
|
||||
Modules can declare Composer package dependencies. This is useful for making a module's requirements explicit. There are two types of dependencies you can define: requirements and suggestions.
|
||||
|
||||
- **Requirements**: Packages that are essential for the module to function correctly.
|
||||
- **Suggestions**: Packages that are recommended but not strictly necessary.
|
||||
|
||||
These dependencies are defined in the module's main class by implementing the `getComposerRequirements()` and `getComposerSuggestions()` methods from the `ModuleContract` interface. These methods should return an array of `ModulePackage` objects.
|
||||
|
||||
**Note:** Declaring a dependency does not automatically install it. This feature is for informational purposes, helping developers understand the module's dependencies.
|
||||
|
||||
Here is an example of how to declare a required package and a suggested module dependency:
|
||||
|
||||
```php
|
||||
// app/Modules/Example/ExampleModule.php
|
||||
|
||||
use App\Modules\Core\ModulePackage;
|
||||
use App\Modules\Core\ModulePackageType;
|
||||
|
||||
// ...
|
||||
|
||||
public function getComposerRequirements(): array
|
||||
{
|
||||
return [
|
||||
new ModulePackage(
|
||||
type: ModulePackageType::PACKAGE,
|
||||
name: 'spatie/laravel-translatable',
|
||||
message: 'This package is used for translatable models.',
|
||||
version: '^8.0'
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
public function getComposerSuggestions(): array
|
||||
{
|
||||
return [
|
||||
new ModulePackage(
|
||||
type: ModulePackageType::MODULE,
|
||||
name: 'OtherModule',
|
||||
message: 'This module provides additional related functionality.'
|
||||
)
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
##### Conventional Structure
|
||||
|
||||
Following the conventional structure is essential for the automatic discovery mechanism to work.
|
||||
|
||||
```
|
||||
app/Modules/Example/
|
||||
├── Configs/
|
||||
│ └── example-config.php
|
||||
├── Database/
|
||||
│ ├── Migrations/
|
||||
│ │ └── 2025_09_22_000000_create_example_table.php
|
||||
│ └── Seeders/
|
||||
│ └── ExampleSeeder.php
|
||||
├── Http/
|
||||
│ ├── Controllers/
|
||||
│ └── Requests/
|
||||
├── Models/
|
||||
│ └── Example.php
|
||||
├── Repositories/
|
||||
├── Resources/
|
||||
│ ├── Lang/
|
||||
│ └── Views/
|
||||
├── Routes/
|
||||
│ └── example-routes.php
|
||||
├── example-helpers.php
|
||||
└── ExampleModule.php
|
||||
```
|
||||
|
||||
#### Creating a New Module
|
||||
|
||||
The `Core` module provides an Artisan command to simplify the creation of new modules.
|
||||
|
||||
1. **Run the `make:module` command**:
|
||||
|
||||
```bash
|
||||
php artisan make:module NewFeature
|
||||
```
|
||||
|
||||
This command will scaffold a new module in `app/Modules/NewFeature` with the necessary directory structure and default files, including the `NewFeatureModule.php` class, a model, controller, repository, and migration.
|
||||
|
||||
You can use the `--plain` option to create a module with only the main module class and an empty directory.
|
||||
|
||||
```bash
|
||||
php artisan make:module NewFeature --plain
|
||||
```
|
||||
|
||||
2. **Add Components**: Add any additional migrations, seeders, models, controllers, etc., to your module following the structure outlined above.
|
||||
|
||||
3. **Enable the Module**: Ensure the `isEnabled()` method in your module class returns `true`.
|
||||
|
||||
```php
|
||||
// app/Modules/NewFeature/NewFeatureModule.php
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
Then, in your module's config file:
|
||||
|
||||
```php
|
||||
// app/Modules/NewFeature/Configs/new-feature-config.php
|
||||
<?php
|
||||
|
||||
return [
|
||||
'enabled' => env('NEW_FEATURE_MODULE_ENABLED', true),
|
||||
];
|
||||
```
|
||||
|
||||
#### Usage via Helpers
|
||||
|
||||
Use the global helper functions to interact with modules throughout the application.
|
||||
|
||||
- **Get the Module Repository**:
|
||||
```php
|
||||
$repository = modular();
|
||||
```
|
||||
|
||||
- **Get All Enabled Modules**:
|
||||
```php
|
||||
$enabledModules = modules(); // Returns a Collection of BaseModule objects
|
||||
|
||||
foreach ($enabledModules as $module) {
|
||||
echo $module->name;
|
||||
}
|
||||
```
|
||||
|
||||
- **Get a Specific Module Instance**:
|
||||
```php
|
||||
$invoiceModule = module('Invoice'); // Returns instance of InvoiceModule
|
||||
|
||||
if ($invoiceModule->isEnabled()) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
- **Get the Modules Path**:
|
||||
```php
|
||||
$path = modules_path('Invoice/Database'); // app/Modules/Invoice/Database
|
||||
```
|
||||
Reference in New Issue
Block a user