Pular para o conteúdo principal

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étodoRetornoDescrição
identifier()stringSlug único (kebab-case). Usado em rotas, cache, middleware module:{id} e na tabela site_modules. Ex.: 'indoor-location'
name()stringNome de exibição (a marca). Ex.: 'Synapsys'
description()stringDescrição curta. Ex.: 'BLE-based indoor location tracking'
version()stringVersã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 ≠ name

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étodoRetornoPropó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)voidRegistra tarefas agendadas
reportHandlers() retorna ReportContract

Apesar 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âmetroTipoDescrição
identifierstringSlug único dentro do módulo (kebab-case)
namestringRótulo exibido na UI de configuração
descriptionstringExplicação curta do que o recurso faz
defaultEnabledboolValor 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âmetroTipoDescrição
namestringSlug único dentro do módulo. Vira ionguard_{module-identifier}_{name}_{deploy}
commandstringComando artisan a invocar (envolvido como php artisan {command})
envarray<string,string>Variáveis de ambiente extras mescladas sobre o .env da plataforma. Padrão: []
restartstringPolítica de restart do Docker. Padrão: 'unless-stopped'
stopGracePeriodSecondsint|nullSegundos 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();
}
Guard clause obrigatório

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 via ModuleRegistry).
  • 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