Pular para conteúdo

Extensions: Registro e Lifecycle

Esta página detalha a direção arquitetural esperada pelo ADR-0004.

O objetivo é separar com clareza:

  • register(): declaração estrutural;
  • boot(): integração de runtime;
  • metadata: identidade e dependências da extension;
  • interfaces públicas: como uma extension expõe capacidades para outras extensions e terceiros.

Estrutura e metadata

O padrão atual de diretório e metadata é contrato do framework.

Exemplo educacional:

<?php

namespace local_middag\extensions\ecommerce;

use local_middag\base\extension;

final class ecommerce_extension extends extension
{
    public const EXTENSION_IDNUMBER = 'ecommerce';
    public const GROUP = 'integrations';
    public const PRIORITY = 20;
    public const REQUIRES = ['webhooks'];
}

Quando usar register()

Use register() para declarar bindings e parâmetros.

Exemplo educacional:

<?php

public function register(ContainerInterface $container): void
{
    $container->register(store_sync_service::class, store_sync_service::class);
}

Não use register() para:

  • acessar DB;
  • ler config operacional do Moodle;
  • resolver serviços;
  • chamar outra extension.

Quando usar boot()

Use boot() para integração de runtime.

Exemplo educacional:

<?php

public function boot(): void
{
    middag::add_action('middag/extension/ecommerce/order_processed', static function (object $payload): void {
        // reação pública
    });
}

Também é válido registrar listeners de signals, filters públicos e outros pontos reativos da extension nessa fase.

Como uma extension expõe capacidade

Facade própria da extension

Quando a integração for simples e estática, a extension pode expor facade própria.

<?php

namespace local_middag\extensions\sentry\facade;

use local_middag\base\facade;
use local_middag\extensions\sentry\service\sentry_service;

final class sentry extends facade
{
    public static function get_facade_accessor(): string
    {
        return sentry_service::class;
    }
}

Estabilidade

A facade da extension pode ser pública. A estabilidade dela pertence à própria extension, não ao core do MIDDAG.

Contract @api e DI

Quando a integração for estrutural, prefira contract @api e DI.

<?php

namespace local_middag\extensions\reports\service;

use local_middag\extensions\ecommerce\contract\store_report_provider_interface;

final class report_service
{
    public function __construct(
        private store_report_provider_interface $provider,
    ) {}
}

Hook público ou dispatch()

Quando a integração for reativa, prefira hooks públicos, filters ou dispatch().

<?php

middag::add_action('middag/extension/ecommerce/order_processed', static function (object $payload): void {
    // integração reativa
});

Como uma extension não deve consumir outra extension

Evite chamar regra de negócio interna de outra extension por classe concreta ou via extension_service.

Exemplo a evitar:

<?php

$extension = $extension_service->get('ecommerce');
$extension->process_order(10);

Prefira uma interface pública:

<?php

\local_middag\extensions\sentry\facade\sentry::capture('order_processed');

ou:

<?php

middag::dispatch(new \local_middag\extensions\ecommerce\signal\order_processed_signal(
    order_id: 10,
    customer_id: 99,
    status: 'processed',
));

Regra prática

  • quero declarar estrutura -> register()
  • quero integrar runtime -> boot()
  • quero expor consumo simples -> facade própria
  • quero expor integração estrutural -> contract @api
  • quero expor integração reativa -> dispatch(), action hook ou filter