Module Dependencies
AutoCom's module system includes robust dependency management that ensures modules load in the correct order and fail gracefully when dependencies are missing.
How Dependencies Work
When the application boots, the ModuleLoaderService:
- Discovers all modules in the
/modulesdirectory - Builds a dependency graph from
module.jsonfiles - Validates that all dependencies are available and enabled
- Detects circular dependencies
- Sorts modules topologically (dependencies load first)
- Loads modules in the calculated order
Declaring Dependencies
Dependencies are declared in your module.json file:
{
"name": "StoreShopify",
"alias": "store-shopify",
"dependencies": {
"modules": ["Core", "Orders"],
"backend": {
"php": "^8.2",
"packages": ["guzzlehttp/guzzle"]
},
"frontend": {
"packages": ["@shopify/polaris"]
}
}
}
Module Dependencies
The dependencies.modules array lists other AutoCom modules that must be loaded before this module:
{
"dependencies": {
"modules": ["Core", "Orders", "Products"]
}
}
Rules:
- Dependencies are loaded in order (Core → Orders → Products → YourModule)
- If a dependency is missing or disabled, the module won't load
- Core modules with missing dependencies cause fatal errors
- Non-core modules are skipped with a warning
Backend Dependencies
Specify PHP version and Composer packages:
{
"dependencies": {
"backend": {
"php": "^8.2",
"packages": [
"guzzlehttp/guzzle",
"league/csv"
]
}
}
}
When you enable a module, the system will:
- Check if the PHP version requirement is met
- Check if declared Composer packages are installed
- Offer to install missing packages automatically
Frontend Dependencies
Specify npm packages:
{
"dependencies": {
"frontend": {
"packages": [
"@tanstack/react-query",
"recharts"
]
}
}
}
When you enable a module, the system will:
- Check if declared npm packages are installed
- Offer to install missing packages automatically
Package Dependency Management
Checking Package Dependencies
Use the module:packages command to check all package dependencies:
# Check all enabled modules
php artisan module:packages
# Check a specific module
php artisan module:packages --module=StoreShopify
# Output as JSON
php artisan module:packages --json
Example output:
Package Dependency Report
PHP Version: 8.2.15
+----------------+-----+------------------+-------------+--------+
| Module | PHP | Composer Missing | npm Missing | Status |
+----------------+-----+------------------+-------------+--------+
| Core | ✓ | 0 | 0 | OK |
| Orders | ✓ | 0 | 0 | OK |
| StoreShopify | ✓ | 1 | 1 | MISSING|
+----------------+-----+------------------+-------------+--------+
Missing Packages:
StoreShopify:
composer: guzzlehttp/guzzle
npm: @shopify/polaris
To install all missing packages:
composer require guzzlehttp/guzzle
cd frontend && npm install @shopify/polaris
Or run: php artisan module:packages --install
Installing Missing Packages
Automatically install all missing packages:
php artisan module:packages --install
Install when enabling a module:
# Will prompt to install if packages are missing
php artisan module:enable StoreShopify
# Automatically install without prompting
php artisan module:enable StoreShopify --install-packages
# Skip package check entirely
php artisan module:enable StoreShopify --skip-packages
How Package Detection Works
The system checks:
- Composer packages: Reads
composer.lockin the backend directory - npm packages: Reads
package-lock.jsonin the frontend directory - PHP version: Compares against
PHP_VERSIONconstant
This means packages must be properly installed (not just listed in package.json/composer.json).
Dependency Validation
Missing Dependencies
If a module declares a dependency that isn't available:
[ERROR] Module 'StoreShopify' requires module 'Orders' which is not available or not enabled
For core modules: This is a fatal error and the application won't start.
For non-core modules: The module is skipped and a warning is logged.
Circular Dependencies
The system detects circular dependencies:
ModuleA depends on ModuleB
ModuleB depends on ModuleC
ModuleC depends on ModuleA ← Circular!
This produces:
[CRITICAL] Circular dependency detected: ModuleA -> ModuleB -> ModuleC -> ModuleA
Resolution: Refactor your modules to remove the circular reference. Consider:
- Moving shared code to Core
- Creating a new shared module
- Using events instead of direct dependencies
Dependency Graph
Viewing the Graph
Use the CLI to visualize dependencies:
php artisan module:list --graph
Output:
Dependency Graph:
Core
└── (no dependencies)
Orders
└── Core
Products
└── Core
Customers
└── Core
StoreShopify
└── Core, Orders
Workflows
└── Core
Reverse Dependencies (dependents):
Core
└── used by: Orders, Products, Customers, StoreShopify, Workflows
Orders
└── used by: StoreShopify
Programmatic Access
Access the dependency graph in code:
use App\Core\Services\ModuleLoaderService;
$loader = app(ModuleLoaderService::class);
// Get the dependency graph
$graph = $loader->getDependencyGraph();
// ['Orders' => ['Core'], 'StoreShopify' => ['Core', 'Orders'], ...]
// Get all dependencies of a module (recursive)
$allDeps = $loader->getAllDependencies('StoreShopify');
// ['Core', 'Orders']
// Get modules that depend on this module
$dependents = $loader->getDependents('Core');
// ['Orders', 'Products', 'Customers', 'StoreShopify', 'Workflows']
Load Order
Modules are loaded using topological sorting (Kahn's algorithm):
- Modules with no dependencies load first
- Within the same "level", modules sort by
priority - Lower priority number = loads earlier
Example Load Order
Given these modules:
| Module | Priority | Dependencies |
|---|---|---|
| Core | 0 | - |
| Orders | 10 | Core |
| Products | 15 | Core |
| StoreShopify | 50 | Core, Orders |
Load order: Core → Orders → Products → StoreShopify
Debugging Load Order
Check the logs to see the calculated order:
ModuleLoader: Calculated load order ["Core", "Orders", "Products", "StoreShopify"]
Best Practices
1. Always Depend on Core
Unless your module is Core itself, always include it:
{
"dependencies": {
"modules": ["Core"]
}
}
2. Minimize Dependencies
Only declare modules you directly use:
// ❌ Don't include transitive dependencies
{
"dependencies": {
"modules": ["Core", "Orders", "Products", "Customers"]
}
}
// ✅ Only direct dependencies
{
"dependencies": {
"modules": ["Orders"] // Orders already depends on Core
}
}
3. Use Appropriate Priority
| Type | Recommended Priority |
|---|---|
| Core infrastructure | 0-10 |
| Business logic (Orders, Products) | 10-30 |
| Reseller modules | 30-50 |
| Store integrations | 50-70 |
| Channel integrations | 70-90 |
| AI/Automation | 90-100 |
4. Document Dependencies
Add comments in your service provider:
/**
* StoreShopify Module
*
* Depends on:
* - Core: Base models and services
* - Orders: Order creation and management
*/
class StoreShopifyServiceProvider extends ServiceProvider
{
// ...
}
5. Use Contracts for Loose Coupling
Instead of depending on concrete modules, use contracts:
// In your module
use App\Core\Contracts\StoreProviderContract;
class MyService
{
public function __construct(
private StoreProviderContract $store
) {}
}
This allows your module to work with any store provider (Shopify, WooCommerce, etc.).
Handling Optional Dependencies
For features that enhance functionality when a module is present:
use App\Core\Services\ModuleLoaderService;
class MyService
{
public function doSomething()
{
$loader = app(ModuleLoaderService::class);
// Check if optional module is loaded
if ($loader->isModuleLoaded('Workflows')) {
// Use workflow features
$this->triggerWorkflow();
}
}
}
Or use class existence check:
if (class_exists(\Modules\Workflows\Services\WorkflowEngine::class)) {
// Workflow module is available
}
Next Steps
- Learn about Module Lifecycle events
- Review CLI Commands for managing dependencies
- See Module Structure for configuration options