title: Contratos: Camada de Extensão e Classes-Base description: O papel das classes-base como estrutura estável para herança e extensão por extensions.
Camada de Extensão e Classes-Base¶
A Camada de Extensão Controlada (classes/base) fornece a infraestrutura estável necessária para que as Extensions possam ser construídas em conformidade com o modelo arquitetural do local_middag.
O que é¶
É um conjunto de classes abstratas e componentes base que oferecem uma implementação parcial de funcionalidades comuns (ex: lifecycle, registro de hooks, tratamento de erros).
Diferente da API Pública Estável (focada em consumo estático ou DI), as Classes-Base são focadas em herança segura. Elas definem o "esqueleto" de como uma Extension, um Controller ou um Handler deve se comportar.
Por que existe¶
Sem classes-base padronizadas:
1. Inconsistência: Cada extension implementaria seu próprio register() ou boot() de forma diferente.
2. Acoplamento aos Internals: Os desenvolvedores seriam tentados a herdar diretamente das classes do núcleo (framework/), o que quebraria na primeira refatoração.
3. Complexidade: O desenvolvedor da extension teria que reescrever toda a lógica de descoberta e integração com o Kernel repetidamente.
As classes-base oferecem um Contrato de Herança Stável, permitindo que o framework evolua internamente enquanto protege os métodos que as extensões sobrescrevem.
Princípios de Design¶
- Liskov Substitution Principle (LSP): Subclasses (extensions) devem poder ser tratadas como a classe-pai (base) sem quebrar o comportamento do sistema. Se a classe-base espera um
voidnoboot(), a extensão não deve retornar algo que mude essa premissa. - Template Method Pattern: O framework frequentemente usa classes-base para definir um fluxo fixo (ex: o
loaderchama oboot) e deixa ganchos (methods) para as extensões preencherem a lógica específica. - Herança vs Composição: O framework favorece a composição em seu núcleo, mas utiliza herança nas fronteiras (camada de extensão) para simplificar a vida do desenvolvedor e garantir que as convenções do MIDDAG sejam seguidas.
Decisões de design¶
- Não é API Pública Ampla: Estar na camada de extensão não significa ser uma API para consumo irrestrito via estático. O objetivo é a herança.
- Estabilidade de Major Version: Assim como os Contracts
@api, as Classes-Base seguem as regras de versionamento. Mudanças em métodos protegidos (protected) ou públicos das classes-base são tratadas como breaking changes. - Encapsulamento de Lifecycle: Classes como
abstract_extensionabstraem a complexidade do Container de DI, fornecendo métodos ergonômicos para registro e boot.
O que não é¶
- Não é Helper Genérico: Classes utilitárias sem estado devem ser serviços ou traits, não classes-base.
- Não é Lógica de Negócio: Uma classe-base nunca deve conter lógica específica de uma extension (ex: "se for ecommerce, faça isso"). Se a lógica é comum a todos, ela pertence ao framework; se é específica, pertence à extension concreta.
- Não é Contract: Contracts (
interfaces) dizem o que fazer; Classes-Base fornecem a infraestrutura base de como começar.
Perspectiva para builders de extensions¶
Como desenvolvedor de extension:
1. Herde Sempre da Base: Nunca herde diretamente de local_middag\framework\*. Procure a classe correspondente em local_middag\base\*.
2. Sobrescreva com Intenção: Ao sobrescrever métodos como register() ou boot(), saiba que você está participando de um ciclo coordenado pelo Kernel.
3. Respeite os Tipos: Se a assinatura da classe-base pede um objeto item_entity, sua extensão não deve tentar usar um stdClass ou um array.
Exemplo ilustrativo¶
Uso da classe-base para criar uma nova Extension:
namespace local_middag\extensions\my_module;
use local_middag\base\extension; // Camada de Extensão Controlada
use Symfony\Component\DependencyInjection\ContainerInterface;
final class my_extension extends extension {
// Implementando contrato de lifecycle herdado
public function register(ContainerInterface $container): void {
// Registro de bindings
}
public function boot(): void {
// Inicialização de runtime
}
}
Neste exemplo, a classe-base extension cuida de toda a descoberta pelo extension_loader, permitindo que o desenvolvedor foque apenas no conteúdo dos métodos register e boot.