Sistema de Módulos
O ION Guard utiliza uma arquitetura modular onde funcionalidades são encapsuladas em módulos independentes. Cada módulo é auto-contido — possui suas próprias models, migrations, rotas, controllers, páginas frontend, alert handlers, report handlers e workers.
Visão geral
Core (app/) Módulo (app/Modules/IndoorLocation/)
├── ModuleRegistry ├── module.json
├── ModuleServiceProvider ├── IndoorLocationServiceProvider
├── AlertHandlerRegistry ├── IndoorLocationModule (implements ModuleContract)
├── ReportServiceProvider ├── Models/, Actions/, Services/
├── EnsureModuleEnabled middleware ├── Http/Controllers/, routes/
└── module-loader.ts (frontend) ├── Alerts/Handlers/, Reports/Handlers/
├── database/migrations/, factories/
└── resources/js/ (pages, locales)
Lifecycle do módulo
1. Discovery
O ModuleServiceProvider (core) escaneia app/Modules/*/ buscando service providers:
app/Modules/{ModuleName}/{ModuleName}ServiceProvider.php
Módulos listados em config/modules.php → disabled são ignorados.
2. Register
O ServiceProvider do módulo é registrado pelo Laravel. No método register():
- Cria a instância do módulo (
new IndoorLocationModule()) - Registra no
ModuleRegistryvia$registry->register($module) - Faz merge de configs (
$this->mergeConfigFrom(...)) - Registra singletons e bindings
3. Boot
No método boot() do ServiceProvider:
- Carrega rotas (web e API) com middleware
module:{identifier} - Carrega migrations do diretório do módulo
- Registra morph maps para relações polimórficas
- Registra policies e gates
- Escuta eventos do domínio
- Registra relacionamentos dinâmicos em models do core
4. Schedule
O arquivo routes/console.php (core) itera todos os módulos registrados e chama $module->schedule($schedule), permitindo que cada módulo registre seus jobs agendados.
ModuleContract
Todo módulo implementa App\Contracts\ModuleContract. Na prática, os módulos estendem App\Contracts\BaseModule, que já implementa o contrato inteiro com padrões vazios — deixando obrigatórios apenas os quatro métodos de identidade (identifier, name, description, version). Referência completa em Contrato de Módulo.
Identidade
| Método | Retorno | Exemplo |
|---|---|---|
identifier() | string | 'indoor-location' |
name() | string | 'Synapsys' |
description() | string | 'BLE-based indoor location tracking...' |
version() | string | '1.0.0' |
scope() | string | 'site' ou 'global' |
Extensões
| Método | Retorno | Propósito |
|---|---|---|
alertHandlers() | list<class-string> | Classes de alert handlers a registrar |
alertTypes() | list<string> | Tipos de alerta que o módulo define |
eventTypes() | list<string> | Tipos de evento que o módulo emite |
reportHandlers() | list<class-string> | Classes de report handlers (implementam ReportContract) |
features() | list<ModuleFeature> | Sub-recursos habilitáveis/desabilitáveis por site |
morphMap() | array<string, class-string> | Morph map para relações polimórficas |
policies() | array<class-string, class-string> | Model → Policy bindings |
gates() | array<string, Closure> | Gates de autorização |
seeders() | list<class-string> | Seeders para dados de demonstração |
workers() | list<WorkerDefinition> | Processos long-running (MQTT listeners, etc.) |
schedule(Schedule) | void | Registrar tarefas agendadas |
Scope: site vs global
| Scope | Comportamento |
|---|---|
site | Habilitado por site via tabela site_modules. Middleware module:{id} verifica se está ativo para o site atual |
global | Sempre ativo para todos os sites. Não requer habilitação individual |
O ModuleRegistry oferece métodos scope-aware:
$registry->isEnabledForSite('indoor-location', $siteId); // bool
$registry->enabledModuleIdentifiersForSite($siteId); // Collection
$registry->alertTypesForSite($siteId); // array (só módulos habilitados)
$registry->isFeatureEnabledForSite('indoor-location', 'cold-room', $siteId); // bool
Sub-recursos (features)
Além de habilitar/desabilitar o módulo inteiro por site, um módulo pode expor sub-recursos habilitáveis individualmente via features(). Cada sub-recurso é um ModuleFeature (identifier, name, description, defaultEnabled), persistido em site_modules.settings.features.{identifier}. É assim que o Synapsys expõe panic-button, asset-tracking e cold-room (Câmara Fria). Detalhes em Contrato de Módulo.
module.json
Cada módulo tem um module.json na sua raiz com metadados:
{
"identifier": "indoor-location",
"name": "Synapsys",
"version": "1.0.0",
"description": "BLE-based indoor location tracking with panic alerts...",
"scope": "site",
"authors": [
{ "name": "Iongrade", "email": "dev@iongrade.com" }
],
"requires": [],
"laravel": "^12.0"
}
Estrutura de diretórios de um módulo
app/Modules/IndoorLocation/
├── module.json # Metadados
├── IndoorLocationServiceProvider.php # Registration + Boot
├── IndoorLocationModule.php # Implementa ModuleContract
├── Actions/ # Business logic
├── Alerts/Handlers/ # Alert handler implementations
├── Reports/Handlers/ # Report handler implementations
├── Console/Commands/ # Artisan commands
├── Contracts/ # Interfaces do módulo
├── Drivers/ # Drivers de hardware
├── Enums/ # Enums específicos
├── Http/
│ ├── Controllers/Web/ # Controllers Inertia
│ ├── Controllers/Api/V1/ # Controllers API
│ ├── Middleware/ # Middlewares do módulo
│ └── Requests/ # Form Requests
├── Jobs/ # Queued jobs
├── Models/ # Eloquent models
├── Policies/ # Authorization policies
├── Services/ # Business services
├── config/ # Config files
├── database/
│ ├── migrations/ # Migrations
│ ├── factories/ # Model factories
│ └── seeders/ # Demo seeders
├── routes/
│ ├── web.php # Web routes
│ └── api.php # API routes
├── resources/js/
│ ├── entry.ts # Frontend entry point
│ ├── pages/ # Inertia pages
│ └── locales/ # Traduções i18n
└── docs/ # Documentação do módulo
Frontend modular
Cada módulo tem um entry.ts que exporta:
- Pages — Páginas Inertia auto-descobertas via
import.meta.glob registerLocales()— Registra traduções i18n no runtimeregisterSlots()— Registra componentes em slots extensíveis do core
O core carrega os entry points via module-loader.ts, integrando as páginas e traduções automaticamente.
Middleware module:{identifier}
Todas as rotas do módulo usam o middleware EnsureModuleEnabled:
Route::middleware(['auth', 'module:indoor-location'])->group(function () {
// Rotas só acessíveis se o módulo estiver habilitado para o site
});
Para módulos scope: 'global', o middleware sempre passa. Para scope: 'site', ele verifica a tabela site_modules.
Próximos passos
- Contrato de Módulo — Referência completa de todos os métodos
- Criando um Módulo — Guia passo a passo
- Frontend Modular — Como o frontend carrega módulos
- Alert Handlers — Criando handlers de alerta
- Report Handlers — Criando handlers de relatório