Pular para o conteúdo principal

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 ModuleRegistry via $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étodoRetornoExemplo
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étodoRetornoPropó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)voidRegistrar tarefas agendadas

Scope: site vs global

ScopeComportamento
siteHabilitado por site via tabela site_modules. Middleware module:{id} verifica se está ativo para o site atual
globalSempre 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 runtime
  • registerSlots() — 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