Comunicação Entre Módulos
Como módulos conversam, dependem uns dos outros e controlam sua instalação.
Existem dois cenários distintos que precisam de abordagens diferentes:
Dependência de instalação — impedir que o módulo seja instalado sem que outro já esteja presente.
Dependência de código — usar classes de outro módulo em runtime (injeção de dependência).
Dependência de instalação
Use quando seu módulo não pode ser instalado sem que outro módulo já esteja presente. O sistema verifica automaticamente — basta declarar as dependências no plugin.json. Nenhum código PHP é necessário.
Como declarar — plugin.json
Crie o arquivo plugin.json na raiz do seu módulo com o campo requires.modules:
Arquivo: src/Modules/SeuModulo/plugin.json
{
"name": "sweflow/module-fatura",
"description": "Módulo de emissão de faturas",
"version": "1.0.0",
"requires": {
"modules": ["Usuario", "Email"]
}
}
O PluginManager lê o plugin.json automaticamente antes de instalar. Se algum módulo da lista não estiver instalado, a instalação é bloqueada com mensagem clara — sem nenhum código PHP no módulo.
O que acontece ao tentar instalar sem a dependência
Se o módulo Email não estiver instalado e alguém tentar instalar o módulo Fatura:
# Tentativa de instalar sem a dependência
php vupi make:module Fatura
php vupi migrate
# Erro retornado:
# Não é possível instalar o módulo 'Fatura', pois é necessário ter instalado o(s) módulo(s) 'Email'.
# Faça a instalação do módulo Email antes de tentar instalar este módulo novamente.
Via marketplace, o erro aparece na resposta da API de instalação com status 422.
Onde o sistema verifica
O PluginManager verifica as dependências em dois lugares:
| Local | O que verifica |
|---|---|
src/Modules/NomeModulo/ | Módulos nativos e clonados do repositório |
storage/plugins_registry.json | Módulos instalados via marketplace com enabled: true |
plugin.json completo
O arquivo suporta outros campos além de requires:
{
"name": "sweflow/module-fatura",
"description": "Módulo de emissão de faturas",
"version": "1.2.0",
"requires": {
"modules": ["Usuario", "Email"]
},
"provides": ["fatura", "billing"],
"extra": {
"vupi.us": {
"providers": ["Src\\Modules\\Fatura\\FaturaProvider"]
}
}
}
| Campo | Descrição |
|---|---|
requires.modules | Módulos que precisam estar instalados antes deste |
provides | Capacidades que este módulo oferece (usado pelo sistema de capabilities) |
extra.vupi.us.providers | Provider PHP para lifecycle hooks avançados (onInstall, onEnable, etc.) |
Dependência de código (runtime)
Use quando seu módulo precisa usar classes de outro módulo em tempo de execução. O Container resolve as dependências automaticamente via injeção no construtor.
Dependência obrigatória
Use quando seu módulo não funciona sem o outro. Se o módulo dependente for removido, o sistema para com erro claro.
use Src\Modules\Usuario\Services\UsuarioService;
class FaturaService
{
// O Container injeta UsuarioService automaticamente.
// Se o módulo Usuario não existir, o Container lança NotFoundException.
public function __construct(
private UsuarioService $usuarioService
) {}
public function emitir(string $userUuid): void
{
$usuario = $this->usuarioService->buscarPorUuid($userUuid);
// ...
}
}
Dependência opcional
Use quando seu módulo pode funcionar sem o outro. O Container injeta null se o módulo não existir — use o operador ?-> (nullsafe) para chamar métodos com segurança.
use Src\Kernel\Contracts\EmailSenderInterface;
class FaturaService
{
// '?' torna o parâmetro nullable — Container injeta null se não encontrar
public function __construct(
private ?EmailSenderInterface $email = null
) {}
public function processar(): void
{
$this->salvarNoBanco();
// Operador nullsafe '?->': se $email for null, a linha é ignorada sem erro
$this->email?->sendCustom('[email protected]', 'Fatura', '<p>Gerada!</p>');
}
}
Resumo — qual abordagem usar?
| Situação | Abordagem |
|---|---|
| Impedir instalação sem o módulo dependente | onInstall() no Provider + plugin.json |
| Usar classes de outro módulo (obrigatório) | Type hint no construtor (sem ?) |
| Usar classes de outro módulo (opcional) | Type hint nullable no construtor (?Tipo) |
| Verificar se módulo está ativo em runtime | is_dir() ou leitura do plugins_registry.json |
Nunca acesse a tabela de outro módulo diretamente via SQL. Sempre use o Service do outro módulo para obter dados. Isso garante que as regras de negócio do módulo dependente sejam respeitadas.