Referência v3
Voltar à plataforma

Comunicação Entre Módulos

Como módulos conversam, dependem uns dos outros e controlam sua instalação.

Dois tipos de dependência

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

json
{
  "name": "sweflow/module-fatura",
  "description": "Módulo de emissão de faturas",
  "version": "1.0.0",
  "requires": {
    "modules": ["Usuario", "Email"]
  }
}
É só isso.

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:

bash
# 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:

LocalO que verifica
src/Modules/NomeModulo/Módulos nativos e clonados do repositório
storage/plugins_registry.jsonMódulos instalados via marketplace com enabled: true

plugin.json completo

O arquivo suporta outros campos além de requires:

json
{
  "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"]
    }
  }
}
CampoDescrição
requires.modulesMódulos que precisam estar instalados antes deste
providesCapacidades que este módulo oferece (usado pelo sistema de capabilities)
extra.vupi.us.providersProvider 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.

php
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.

php
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çãoAbordagem
Impedir instalação sem o módulo dependenteonInstall() 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 runtimeis_dir() ou leitura do plugins_registry.json
Regra de ouro

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.