Kernel, Container e Boot¶
O kernel do MIDDAG (local_middag\framework\kernel\kernel) faz a ponte entre o ciclo procedural do Moodle e a arquitetura orientada a objetos do framework. A sua responsabilidade principal é inicializar o container, descobrir recursos do core e das extensions e deixar o ambiente pronto para uso.
O que o kernel coordena¶
- inicialização do router;
- construção do container via
container_factory; - descoberta de extensions, services, facades e types;
- boot das extensions antes da compilação final;
- compilação do grafo de serviços;
- inicialização do
http_kernelpara requests HTTP.
Sequência de boot¶
O boot é iniciado pela facade pública local_middag\middag e segue um fluxo único por request.
sequenceDiagram
participant Moodle as Moodle
participant Facade as local_middag\middag
participant Helper as middag_helper
participant Kernel as kernel
participant Factory as container_factory
participant Loaders as ext/facade/service/type loaders
participant Extensions as extension_service
participant Container as ContainerBuilder
Moodle->>Facade: middag::init()
Facade->>Helper: init()
Helper->>Kernel: init()
alt ja inicializado
Kernel-->>Helper: reutiliza estado atual
else primeira inicializacao
Kernel->>Factory: get_instance()
Factory->>Container: new ContainerBuilder()
Factory->>Loaders: load()
Loaders-->>Container: registra definicoes
Factory->>Extensions: boot_all()
Extensions-->>Container: registra/boota extensoes
Factory->>Container: compile()
Factory->>Container: inject synthetics
Kernel-->>Helper: container pronto
end
Fases do boot¶
1. Descoberta¶
Os loaders localizam classes e pontos de integração no framework e nas extensions:
extension_loader: encontra extensões e executabootstrap.phpquando existir;service_loader: registra classes autowireáveis do framework e das extensions;facade_loader: carrega facades geradas e accessors relacionados;type_loader: registra tipos de item e suas classes de domínio.
2. Boot de extensões¶
Com as definições já registradas, o framework executa o ciclo de vida das extensions para permitir registro adicional antes da compilação final.
3. Compilação¶
O container é compilado e congelado. A partir desse ponto, as definições deixam de ser mutáveis.
4. Injeção sintética¶
Objetos criados fora do container, mas que precisam participar do grafo final, são inseridos depois da compilação. Isso inclui serviços sintéticos de runtime e bridges para objetos do Moodle já existentes no processo.
Depois da compilação
O container deixa de ser um ContainerBuilder mutável. Registro tardio de services, routes ou wiring estrutural deve ser tratado como erro de desenho do ciclo de vida.
Regra prática: container, factory ou new¶
O ADR-0001 define três caminhos válidos, cada um com propósito próprio.
Os exemplos abaixo são educacionais. Eles mostram o padrão esperado de uso e não dependem de uma classe específica já existente no repositório.
Use o container¶
Use o container quando o objeto participa da arquitetura estrutural do framework ou da extension.
Framework
<?php
namespace local_middag\framework\application\service\audit;
use local_middag\framework\contract\repository\item_repository_interface;
final class audit_projection_service
{
public function __construct(
private item_repository_interface $item_repository,
) {}
public function project(int $item_id): void
{
$item = $this->item_repository->find_by_id($item_id);
// logica de projeção
}
}
Aqui o correto é deixar o service ser resolvido pelo container. A dependência é estrutural e deve continuar substituível e testável.
Extension do ecossistema
<?php
namespace local_middag\extensions\ecommerce\service;
use local_middag\framework\contract\repository\item_repository_interface;
final class store_catalog_sync_service
{
public function __construct(
private item_repository_interface $item_repository,
private store_client_factory $store_client_factory,
) {}
public function sync_store(int $store_id, string $provider): void
{
$client = $this->store_client_factory->create($provider);
$catalog = $client->fetch_catalog($store_id);
// persistencia e sincronizacao
}
}
Na extension, o padrão esperado continua sendo DI. O service pode ser autodescoberto pelo loader sem precisar de middag::get() dentro da própria extension.
Plugin terceiro
<?php
use local_middag\framework\contract\repository\item_repository_interface;
use local_middag\middag;
middag::init();
$item_repository = middag::get(item_repository_interface::class);
$item = $item_repository->find_by_id(42);
Para terceiro, o caminho indicado é consumir facade pública e contracts @api quando existirem.
Fora do padrão desejado
Framework e extensions não devem usar middag::get() como atalho rotineiro no lugar de DI. Para plugins terceiros, middag::get() é tolerado como entrada pública, mas resolver classes internas concretas sai das garantias do MIDDAG.
Use factory¶
Use factory quando houver escolha dinâmica entre implementações estruturais diferentes em runtime.
Extension com múltiplos provedores
<?php
namespace local_middag\extensions\ecommerce\service;
use coding_exception;
interface store_client_interface
{
public function fetch_catalog(int $store_id): array;
}
final class woocommerce_store_client implements store_client_interface
{
public function fetch_catalog(int $store_id): array
{
return [];
}
}
final class shopify_store_client implements store_client_interface
{
public function fetch_catalog(int $store_id): array
{
return [];
}
}
final class magento_store_client implements store_client_interface
{
public function fetch_catalog(int $store_id): array
{
return [];
}
}
final class store_client_factory
{
public function __construct(
private woocommerce_store_client $woocommerce,
private shopify_store_client $shopify,
private magento_store_client $magento,
) {}
public function create(string $provider): store_client_interface
{
return match ($provider) {
'woocommerce' => $this->woocommerce,
'shopify' => $this->shopify,
'magento' => $this->magento,
default => throw new coding_exception("Unsupported provider: {$provider}"),
};
}
}
Essa factory faz sentido porque a escolha depende do provedor informado em runtime e cada provedor possui fluxo próprio.
Quando não usar factory
<?php
final class store_payload_dto
{
public function __construct(
public readonly string $provider,
public readonly array $payload,
) {}
}
Se a variação for apenas de dados, factory só adiciona burocracia.
Regra objetiva
Se a factory encapsula uma decisão arquitetural real, ela deve ser service do container. Se apenas esconde new, não deveria existir.
Use new¶
Use new para objetos locais e transitórios.
Framework
<?php
final class item_creation_payload
{
public function __construct(
public readonly string $type,
public readonly string $fullname,
) {}
}
$payload = new item_creation_payload(
type: 'company',
fullname: 'Empresa X',
);
Extension
<?php
final class store_sync_result
{
public function __construct(
public readonly int $processed,
public readonly int $failed,
) {}
}
Plugin terceiro
<?php
$payload = new stdClass();
$payload->externalid = 'shopify-1001';
$payload->status = 'pending';
Nesses casos, o objeto não abre ponto de extensão nem exige governança de ciclo de vida.
Severidade por camada¶
O mesmo princípio vale em todas as camadas, mas a severidade muda.
| Camada | Container como padrão | Desvio |
|---|---|---|
| Framework | obrigatório | erro arquitetural salvo exceção técnica controlada |
| Extensions do ecossistema | esperado | exceção justificada e documentada |
| Plugins terceiros | altamente recomendável | permitido, mas fora das garantias do MIDDAG |
Entrada pública recomendada¶
Quando código fora do núcleo precisar acessar o framework, a ordem recomendada é:
- facade pública quando existir;
- contract
@apiresolvido pela API pública; - integração livre por conta do consumidor, ciente de que isso não é estabilidade garantida.
<?php
use local_middag\framework\contract\repository\item_repository_interface;
use local_middag\middag;
middag::init();
$item_repository = middag::get(item_repository_interface::class);
O que evitar¶
<?php
use local_middag\framework\infrastructure\persistence\repository\item_repository;
$repository = new item_repository(/* dependencias manuais */);
No framework e nas extensions, isso quebra a governança do container. Em plugin terceiro, isso acopla diretamente em detalhes internos e sai das garantias de compatibilidade.