Contrato de Módulo
Todo módulo do ION Guard implementa a interface App\Contracts\ModuleContract. Ela é o contrato único entre o core e o módulo: o core só conhece o módulo através destes métodos. A classe abstrata App\Contracts\BaseModule implementa o contrato inteiro com padrões vazios, deixando obrigatórios apenas os quatro métodos de identidade — todos os demais são opcionais e você sobrescreve só o que o módulo usa.
namespace App\Modules\MeuModulo;
use App\Contracts\BaseModule;
final class MeuModuloModule extends BaseModule
{
public function identifier(): string { return 'meu-modulo'; }
public function name(): string { return 'Meu Módulo'; }
public function description(): string { return 'O que o módulo faz.'; }
public function version(): string { return '1.0.0'; }
// scope() e todos os demais métodos herdam os padrões de BaseModule
}
Identidade
Métodos que descrevem o módulo. identifier(), name(), description() e version() são abstratos em BaseModule — sempre implementados. scope() tem padrão 'site'.
| Método | Retorno | Descrição |
|---|---|---|
identifier() | string | Slug único (kebab-case). Usado em rotas, cache, middleware module:{id} e na tabela site_modules. Ex.: 'indoor-location' |
name() | string | Nome de exibição (a marca). Ex.: 'Synapsys' |
description() | string | Descrição curta. Ex.: 'BLE-based indoor location tracking' |
version() | string | Versão semântica do módulo. Ex.: '1.0.0' |
scope() | string | 'site' (habilitado por site via site_modules) ou 'global' (sempre ativo). Padrão: 'site' |
identifier() é o slug técnico e não muda — ele aparece em rotas, middleware e chaves de banco. name() é o rótulo de marca, que pode mudar sem quebrar nada. No módulo de rastreamento, identifier() = 'indoor-location' mas name() = 'Synapsys'.
Extensões
Métodos opcionais que o core coleta durante o boot para estender a plataforma. Em BaseModule todos retornam array vazio (ou não fazem nada, no caso de schedule()).
| Método | Retorno | Propósito |
|---|---|---|
alertHandlers() | list<class-string<AlertHandlerContract>> | Classes de alert handlers a registrar |
alertTypes() | list<string> | Tipos de alerta que o módulo define |
eventTypes() | list<string> | Tipos de evento de domínio que o módulo emite |
reportHandlers() | list<class-string<ReportContract>> | Classes de report handlers |
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> | Bindings Model → Policy |
gates() | array<string, Closure> | Gates de autorização |
seeders() | list<class-string<Seeder>> | Seeders (dados de demonstração) |
workers() | list<WorkerDefinition> | Processos long-running (listeners MQTT etc.) |
schedule(Schedule $schedule) | void | Registra tarefas agendadas |
reportHandlers() retorna ReportContractApesar do nome do método, as classes retornadas por reportHandlers() implementam a interface App\Contracts\ReportContract (não existe uma interface ReportHandlerContract). Da mesma forma, alertHandlers() retorna classes que implementam App\Contracts\AlertHandlerContract.
Sub-recursos: features()
features() retorna uma lista de App\Contracts\Modules\ModuleFeature — sub-capacidades que um Platform Admin pode ligar/desligar por site, sem desabilitar o módulo inteiro. É assim que o módulo Synapsys expõe panic-button, asset-tracking e cold-room (Câmara Fria).
ModuleFeature é um objeto readonly com estes parâmetros de construtor:
| Parâmetro | Tipo | Descrição |
|---|---|---|
identifier | string | Slug único dentro do módulo (kebab-case) |
name | string | Rótulo exibido na UI de configuração |
description | string | Explicação curta do que o recurso faz |
defaultEnabled | bool | Valor usado quando o site ainda não tem preferência persistida. Padrão: true |
use App\Contracts\Modules\ModuleFeature;
public function features(): array
{
return [
new ModuleFeature(
identifier: 'cold-room',
name: 'Câmara Fria',
description: 'Monitoramento de exposição em ambientes refrigerados.',
defaultEnabled: false,
),
];
}
A persistência fica em site_modules.settings.features.{identifier} como booleano. Quando a chave está ausente, defaultEnabled é usado. Alert handlers e report handlers podem declarar dependência de um sub-recurso via seus métodos feature(); o dispatch central verifica ModuleRegistry::isFeatureEnabledForSite() antes de invocá-los.
Workers: workers()
workers() retorna uma lista de App\Contracts\Workers\WorkerDefinition — processos long-running que a plataforma mantém vivos. Na instalação/desinstalação, a action App\Actions\RegenerateModuleRuntimeCompose materializa cada worker como um container Docker Compose dedicado (espelhando o padrão ionguard_horizon / ionguard_scheduler / ionguard_reverb).
O módulo descreve o quê rodar, nunca como containerizar — imagem, volumes e redes são fornecidos pelo renderizador.
| Parâmetro | Tipo | Descrição |
|---|---|---|
name | string | Slug único dentro do módulo. Vira ionguard_{module-identifier}_{name}_{deploy} |
command | string | Comando artisan a invocar (envolvido como php artisan {command}) |
env | array<string,string> | Variáveis de ambiente extras mescladas sobre o .env da plataforma. Padrão: [] |
restart | string | Política de restart do Docker. Padrão: 'unless-stopped' |
stopGracePeriodSeconds | int|null | Segundos que o Docker espera após SIGTERM antes de SIGKILL. Padrão: 30 |
use App\Contracts\Workers\WorkerDefinition;
public function workers(): array
{
return [
new WorkerDefinition(
name: 'mqtt-listener',
command: 'indoor-location:mqtt-listen',
),
];
}
Tarefas agendadas: schedule()
O arquivo routes/console.php do core itera todos os módulos registrados e chama $module->schedule($schedule) — independente de o módulo estar habilitado para algum site.
use Illuminate\Console\Scheduling\Schedule;
public function schedule(Schedule $schedule): void
{
$schedule->call(function (): void {
foreach (Site::query()->get() as $site) {
if (app(ModuleRegistry::class)->isEnabledForSite('meu-modulo', $site->id)) {
dispatch(new MeuJob($site->id));
}
}
})->everyFiveMinutes();
}
Como schedule() é chamado para todos os módulos registrados independente do estado de habilitação, jobs agendados devem verificar isEnabledForSite() (e, quando aplicável, isFeatureEnabledForSite()) antes de executar para cada site.
Ciclo de coleta pelo core
O core consulta o contrato em momentos distintos:
- Register/Boot (
ModuleServiceProvider):identifier(),name(),scope(),morphMap(),policies(),gates(),seeders()são aplicados; rotas e migrations do módulo são carregadas. - Dispatch de alertas:
alertTypes()/alertHandlers()(filtrados por site e sub-recurso viaModuleRegistry). - Relatórios:
reportHandlers(). - Scheduler (
routes/console.php):schedule(). - Install/Uninstall:
workers()→RegenerateModuleRuntimeCompose.
O ModuleRegistry expõe métodos scope-aware que respeitam habilitação por site e por sub-recurso:
$registry->isEnabledForSite('indoor-location', $siteId); // bool
$registry->enabledModuleIdentifiersForSite($siteId); // Collection
$registry->alertTypesForSite($siteId); // array
$registry->isFeatureEnabledForSite('indoor-location', 'cold-room', $siteId); // bool
Próximos passos
- Sistema de Módulos — Arquitetura e lifecycle
- Criando um Módulo — Guia passo a passo
- Alert Handlers —
AlertHandlerContract - Report Handlers —
ReportContract