Commands, Jobs e Scheduling¶
Esta página detalha a direção arquitetural esperada pelo ADR-0006.
O objetivo é separar com clareza:
- command: o que deve ser feito;
- job: governança da execução quando necessário;
- schedule: quando um gatilho periódico deve acontecer;
- scheduled_task / adhoc_task: adapters do runtime do Moodle.
Regra principal¶
O framework não deve acoplar command diretamente a cron.
O modelo esperado é:
- um
commandrepresenta a unidade de trabalho; - um
scheduledeclara o gatilho periódico daquele trabalho; - um CLI gera ou sincroniza adapters em
classes/task/*e entradas emdb/tasks.php; - o runtime do Moodle continua executando as tasks;
- a lógica principal continua no handler do command ou no orquestrador do framework.
Fluxo esperado¶
flowchart TB
Command["command"]
Schedule["schedule declarativo"]
CLI["CLI de sync/generate"]
Task["generated scheduled_task adapter"]
Moodle["cron do Moodle"]
Orchestrator["cron_orchestrator ou command_dispatcher"]
Handler["command handler"]
Job["job (opcional)"]
Command --> Schedule
Schedule --> CLI
CLI --> Task
CLI --> db["db/tasks.php"]
Task --> Moodle
Moodle --> Orchestrator
Orchestrator --> Handler
Orchestrator --> Job
O que o gerador deve fazer¶
O gerador por CLI deve:
- localizar commands agendáveis e seus schedules declarados;
- gerar ou sincronizar adapters em
classes/task/*; - gerar ou sincronizar as entradas correspondentes em
db/tasks.php; - preservar naming e previsibilidade dos classnames;
- evitar mutação dinâmica em runtime;
- respeitar que o Moodle continua autoridade final sobre customização operacional de cron.
O que o gerador não deve fazer¶
- não deve assumir que todo command é periódico;
- não deve criar task para command sem
scheduleexplícito; - não deve sobrescrever automaticamente customizações operacionais do admin;
- não deve mover regra principal de negócio para a task gerada;
- não deve recriar um scheduler paralelo ao Moodle.
Estrutura esperada¶
Command¶
<?php
namespace local_middag\extensions\ecommerce\command;
final class sync_products_command
{
public function __construct(
public readonly int $store_item_id,
) {}
}
Schedule declarativo¶
O schedule é artefato separado do command.
Exemplo educacional:
<?php
namespace local_middag\extensions\ecommerce\schedule;
use local_middag\extensions\ecommerce\command\sync_products_command;
final class sync_products_schedule
{
public const COMMAND = sync_products_command::class;
public const TASK_CLASS = 'local_middag\\task\\ecommerce_sync_products';
public const MINUTE = '*/15';
public const HOUR = '*';
public const DAY = '*';
public const MONTH = '*';
public const DAYOFWEEK = '*';
public const BLOCKING = false;
public const DISABLED_BY_DEFAULT = false;
}
Separação deliberada
O schedule aponta para o command, mas não se confunde com ele. O mesmo command pode ser executado por cron, por adhoc_task, por dispatch() ou até de forma síncrona.
Generated scheduled_task adapter¶
O adapter gerado em classes/task/* deve ser fino:
<?php
namespace local_middag\task;
use core\task\scheduled_task;
use local_middag\middag;
final class ecommerce_sync_products extends scheduled_task
{
public function get_name(): string
{
return 'E-commerce - Sync products';
}
public function execute(): void
{
middag::dispatch(
new \local_middag\extensions\ecommerce\command\sync_products_command(
store_item_id: 0,
)
);
}
}
O exemplo acima é apenas educacional. Na prática, o adapter pode chamar um cron_orchestrator, um command_dispatcher ou outro serviço de infraestrutura do framework, desde que a regra principal continue fora da task.
Como cada camada deve aplicar¶
Framework¶
O core pode declarar schedules próprios e também manter tasks coringa para intervalos comuns.
Exemplo de uso principal:
Exemplo de regra:
clean_audit_logs_commandtemscheduleexplícito;- o CLI gera
classes/task/clean_audit_logs.php; - a task delega ao framework;
- se houver governança forte, o fluxo pode abrir
job.
Extension do ecossistema¶
A extension declara command e schedule. O CLI sincroniza os adapters necessários.
<?php
namespace local_middag\extensions\webhooks\command;
final class flush_pending_webhooks_command
{
}
<?php
namespace local_middag\extensions\webhooks\schedule;
final class flush_pending_webhooks_schedule
{
public const COMMAND = \local_middag\extensions\webhooks\command\flush_pending_webhooks_command::class;
public const TASK_CLASS = 'local_middag\\task\\webhooks_flush_pending';
public const MINUTE = '*/5';
public const HOUR = '*';
public const DAY = '*';
public const MONTH = '*';
public const DAYOFWEEK = '*';
}
Plugin terceiro¶
O plugin terceiro pode seguir o mesmo modelo, se o framework abrir a geração/sincronização para fora.
Se não seguir o modelo, continua livre para manter db/tasks.php e tasks próprias do jeito tradicional do Moodle.
<?php
// caminho tradicional do Moodle em plugin terceiro
$tasks = [
[
'classname' => \local_partner\task\sync_orders::class,
'blocking' => 0,
'minute' => '*/10',
'hour' => '*',
'day' => '*',
'month' => '*',
'dayofweek' => '*',
],
];
Jobs: quando entram¶
Job não é obrigatório para todo command assíncrono.
Abra job quando houver necessidade de:
- retry controlado;
- deduplicação;
- correlação;
- vínculo explícito com
item,user,course, lote ou sujeito de domínio; - observabilidade operacional.
Exemplo educacional:
<?php
final class send_partner_webhook_command
{
public function __construct(
public readonly int $item_id,
public readonly string $endpoint,
) {}
}
Esse command pode:
- rodar sem
job, se for simples e descartável; - ou abrir
job, se precisar de retry, rastreio e correlação.
Papel do orquestrador de cron¶
Quando existir comportamento dinâmico de cron, o caminho recomendado é um orquestrador do framework, não lógica rica dentro de cada scheduled_task.
Esse orquestrador pode:
- decidir quais commands disparar;
- abrir jobs quando necessário;
- acionar
dispatch()quando o gatilho periódico representar uma ocorrência do framework; - aplicar regras por extension, licença, configuração, janela e prioridade.
Exemplo educacional:
<?php
final class cron_orchestrator
{
public function run(string $task_class): void
{
// resolve schedules aplicáveis
// decide qual command disparar
// decide se abre job
// delega ao dispatcher ou ao command handler
}
}
Tasks coringa do core¶
O core pode oferecer scheduled tasks coringa para intervalos comuns, como:
- a cada 5 minutos;
- a cada 1 hora;
- 2 vezes por dia.
Essas tasks devem:
- ficar desativadas por padrão;
- existir como compatibilidade leve para desenvolvedores;
- permitir registro por hook ou integração simples;
- não substituir o modelo principal baseado em
command + schedule.
Exemplo de hook compatível:
<?php
middag::add_action('middag/cron/every_five_minutes', function (): void {
// integração leve
});
Caminho auxiliar
Tasks coringa são camada auxiliar de compatibilidade. Elas não devem virar o modelo principal do core para trabalho periódico de negócio.
Autoridade final do cron¶
Mesmo com geração por CLI:
- o Moodle continua sendo a autoridade final sobre runtime;
- o admin continua podendo customizar horários;
- o framework não deve sobrescrever automaticamente essas customizações em produção.
Especificação esperada do CLI¶
O CLI de sincronização deve, no mínimo:
- localizar schedules declarativos elegíveis;
- validar referência ao command alvo;
- validar unicidade de
TASK_CLASS; - gerar ou atualizar adapter em
classes/task/*; - gerar ou atualizar bloco correspondente em
db/tasks.php; - preservar comentários ou customizações manuais fora da área gerada, se esse for o modelo adotado;
- falhar de forma explícita em conflito de naming ou de schedule.
Regra prática¶
- quero trabalho relevante e reutilizável ->
command - quero governança operacional ->
job - quero gatilho periódico ->
schedule - quero execução pelo cron do Moodle -> generated
scheduled_task - quero compatibilidade leve por intervalo comum -> task coringa do core, se habilitada