Pular para conteúdo

API Pública e Camada de Extensão

O MIDDAG não trata toda classe reutilizável como API pública. A arquitetura separa explicitamente o que é estável para consumo externo, o que é estável apenas para extensão controlada e o que permanece interno por padrão.

Três zonas de consumo

Zona Público principal Objetivo Estabilidade esperada
API pública estável plugins externos, integrações e extensões consumidoras acesso seguro a capacidades públicas alta
Camada de extensão controlada extensões e integrações que seguem o modelo do framework herança, estruturação e composição guiada alta, mas com escopo controlado
Interno por padrão core do framework liberdade de implementação e refatoração sem garantia externa

Facades

Facades existem para reduzir acoplamento de consumidores externos com classes internas, caminhos de arquivo e detalhes de wiring.

Use facade quando:

  • houver necessidade de acesso estático simples;
  • a capacidade já for tratada como pública pelo core;
  • o objetivo for proteger consumidores contra reorganização interna do framework.

Não use facade como regra universal:

  • nem toda capacidade pública precisa de facade;
  • código interno do framework deve preferir DI;
  • integrações mais estruturadas podem depender de contracts @api ou classes-base.

Quando uma facade existir, ela deve apontar para um accessor estável governado pelo framework, normalmente baseado em resolução pelo container.

Quando preferir facade ou contract @api

Use facade quando:

  • o consumo for externo e simples;
  • a chamada puder ser estática e direta;
  • o objetivo principal for ergonomia e desacoplamento de wiring interno.

Use contract @api quando:

  • a integração exigir DI;
  • o consumidor precisar compor um service próprio;
  • houver necessidade de substituição, implementação alternativa ou decoração suportada.
<?php

use local_middag\middag;

middag::init();

// consumo simples por facade principal
middag::add_action('middag/matricula/created', static function (array $payload): void {
    // reacao simples
});
<?php

namespace local_vendor\local_partner\service;

use local_middag\framework\contract\repository\item_repository_interface;

final class partner_sync_service
{
    public function __construct(
        private item_repository_interface $item_repository,
    ) {}
}

Contracts

Contracts em classes/framework/contract/ não são públicos por localização. O critério real é a anotação e a intenção arquitetural.

  • @api: contract estável para consumo externo, type-hint, DI ou implementação alternativa suportada.
  • @internal: contract interno, livre para reorganização.

Promover um contract para @api faz sentido quando:

  • consumidores externos precisam tipar contra ele;
  • extensões precisam fornecer implementação alternativa suportada;
  • o core quer preservar esse ponto como contrato estável.

Classes-base

As classes em classes/base/ formam a camada de extensão controlada.

Elas existem para:

  • abrir pontos seguros de herança;
  • evitar que extensões dependam de classes internas do framework;
  • padronizar como extensions, controllers, repositories, services, DTOs e outros tipos se conectam ao núcleo.

Elas não significam "liberdade total" sobre o framework inteiro. São pontos de abertura deliberados.

Plugins terceiros também podem adotá-las quando quiserem seguir o modelo arquitetural do MIDDAG de forma mais próxima.

<?php

namespace local_vendor\local_partner\repository;

use local_middag\base\repository;

final class partner_order_repository extends repository
{
    // implementação própria aderente ao modelo do framework
}

Regras por público

Framework interno

  • usa DI e contracts internos;
  • trata facades como interface voltada a consumo externo, não como atalho padrão interno;
  • pode reorganizar internals sem comprometer a API pública.
<?php

namespace local_middag\framework\application\service\revision;

use local_middag\framework\contract\repository\item_repository_interface;

final class revision_capture_service
{
    public function __construct(
        private item_repository_interface $item_repository,
    ) {}
}

Extensions do ecossistema MIDDAG

  • devem preferir classes-base para herança;
  • devem usar contracts @api quando a integração exigir DI;
  • devem preferir facades públicas quando consumirem capacidades já expostas estaticamente.
<?php

namespace local_middag\extensions\ecommerce;

use local_middag\base\extension;

final class ecommerce_extension extends extension
{
    public const EXTENSION_IDNUMBER = 'ecommerce';
}
<?php

namespace local_middag\extensions\ecommerce\service;

use local_middag\framework\contract\repository\item_repository_interface;

final class order_sync_service
{
    public function __construct(
        private item_repository_interface $item_repository,
        private store_client_factory $store_client_factory,
    ) {}
}
<?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;
    }
}

Facade de extension

Uma facade de extension pode ser pública e consumida livremente. A estabilidade dela, porém, pertence à própria extension que a expõe, não ao core do local_middag.

Plugins e integrações de terceiros

  • devem preferir facades quando elas existirem;
  • podem usar contracts @api quando precisarem integrar por DI;
  • não devem depender de classes internas do framework nem inferir publicidade por caminho de arquivo.
<?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);

Uso fora da API pública

Resolver classes concretas internas do framework ou depender de caminhos como classes/framework/... não é integração estável. Isso pode funcionar tecnicamente, mas não é uma garantia do produto.

<?php

use local_middag\framework\infrastructure\persistence\repository\item_repository;

// Evite este padrão em plugins terceiros.
$item_repository = new item_repository(/* dependencias internas */);

Regra prática

Se um tipo não estiver explicitamente exposto como facade pública, contract @api ou classe-base, trate-o como interno por padrão.