Pular para conteúdo

Fronteira com o Moodle

O MIDDAG trata o Moodle como uma plataforma externa com a qual precisa conviver. Por isso, APIs, funções, globais, convenções e classes do Moodle não devem se espalhar livremente pelo framework.

A fronteira arquitetural principal com o Moodle fica em framework/support/moodle/.

O que pertence a essa fronteira

Essa camada existe para:

  • encapsular funções, globais e convenções do Moodle;
  • concentrar compatibilidade entre versões;
  • proteger services, domínio, facades públicas e extensions contra acoplamento procedural;
  • traduzir eventos do Moodle para signals internos do framework quando isso fizer parte do fluxo reativo do MIDDAG.

Regra prática

Se uma classe depende diretamente de função nativa do Moodle, global do Moodle, classe do Moodle ou convenção específica do core, ela está na fronteira com o Moodle e deve ficar nessa camada.

Adapter concreto vs contract

Nem toda abstração dessa fronteira precisa virar contract.

Use apenas adapter concreto quando

  • a classe só organiza o acesso ao Moodle dentro do framework;
  • ninguém precisa substituir esse comportamento como ponto estável;
  • a necessidade principal é encapsular detalhe técnico ou compatibilidade.

Exemplo educacional do framework

<?php

namespace local_middag\framework\support\moodle;

final class enrolment_support
{
    public function user_has_role_in_course(int $user_id, int $course_id, string $role_shortname): bool
    {
        // encapsula chamadas e convenções do Moodle aqui
        return true;
    }
}

Aqui o objetivo é concentrar detalhe técnico do Moodle. Não existe, necessariamente, necessidade de contrato público.

Promova para contract quando

  • o comportamento precisar ser substituído de forma suportada;
  • extensions ou integrações precisarem depender desse ponto por DI;
  • o framework quiser preservar esse ponto como contrato estável.

Exemplo educacional com contract @api

<?php

namespace local_middag\framework\contract\moodle;

/**
 * @api
 */
interface moodle_user_gateway_interface
{
    public function find_user(int $user_id): array;
}
<?php

namespace local_middag\framework\support\moodle;

use local_middag\framework\contract\moodle\moodle_user_gateway_interface;

final class moodle_user_gateway implements moodle_user_gateway_interface
{
    public function find_user(int $user_id): array
    {
        // leitura encapsulada do Moodle
        return [];
    }
}

Aqui o contract passa a fazer sentido porque a integração virou ponto estável de DI e composição.

Regras por público

Framework interno

  • usa a fronteira Moodle como ponto obrigatório de contato;
  • não espalha chamadas diretas ao Moodle em services, domínio e facades públicas;
  • concentra aqui compatibilidade de versão;
  • concentra aqui a tradução de eventos do Moodle para signals internos, quando essa tradução existir.
<?php

namespace local_middag\framework\application\service\course;

use local_middag\framework\support\moodle\enrolment_support;

final class course_access_service
{
    public function __construct(
        private enrolment_support $enrolment_support,
    ) {}

    public function can_view(int $user_id, int $course_id): bool
    {
        return $this->enrolment_support->user_has_role_in_course($user_id, $course_id, 'student');
    }
}

Extensions do ecossistema MIDDAG

  • devem preferir abstrações do framework quando existirem;
  • acesso direto ao Moodle é exceção, não padrão;
  • necessidade recorrente de acesso direto deve ser promovida para a fronteira do framework;
  • eventos do Moodle não devem ser o mecanismo reativo principal da extension quando já existir tradução interna oficial.
<?php

namespace local_middag\extensions\ecommerce\service;

use local_middag\framework\support\moodle\enrolment_support;

final class checkout_visibility_service
{
    public function __construct(
        private enrolment_support $enrolment_support,
    ) {}

    public function should_show_offer(int $user_id, int $course_id): bool
    {
        return $this->enrolment_support->user_has_role_in_course($user_id, $course_id, 'student');
    }
}

Extension fora do padrão desejado

Quando uma extension chamar diretamente função do Moodle para um caso que já possua adapter oficial, ela está contornando a fronteira arquitetural do framework.

Plugins e integrações de terceiros

  • podem acessar o Moodle diretamente;
  • devem preferir abstração do MIDDAG quando ela existir;
  • ao ignorar a fronteira do MIDDAG, saem das garantias de compatibilidade do framework.

Caminho indicado

<?php

use local_middag\framework\support\moodle\enrolment_support;
use local_middag\middag;

middag::init();

$enrolment_support = middag::get(enrolment_support::class);
$allowed = $enrolment_support->user_has_role_in_course($USER->id, $courseid, 'student');

Caminho livre, mas fora da garantia

<?php

global $DB;

$enrolments = $DB->get_records('user_enrolments', ['userid' => $USER->id]);

Fora das garantias do MIDDAG

O plugin terceiro continua livre para falar com o Moodle diretamente, mas o MIDDAG não garante compatibilidade arquitetural nem estabilidade sobre esse caminho.

Observer central do Moodle

O observer central pertence a essa fronteira. Ele não é um mecanismo de domínio; ele é um ponto de entrada da plataforma para dentro do fluxo reativo do framework.

Estratégia inicial

O framework pode começar com um catch_all e sair rapidamente quando nada for consumido.

<?php

$observers = [
    [
        'eventname' => '*',
        'callback' => observer::class . '::catch_all',
        'internal' => false,
    ],
];
<?php

final class observer
{
    public static function catch_all(\core\event\base $event): void
    {
        // TODO: traduzir apenas eventos oficiais do Moodle em signals internos
        // TODO: sair em no-op rápido quando não houver listener tipado
        // TODO: sair em no-op rápido quando não houver hook canônico mapeado com ouvinte
    }
}

Regra prática do observer

  • o observer central recebe eventos do Moodle;
  • a fronteira decide se existe tradução oficial para signal interno;
  • se não existir tradução, listener tipado ou hook derivado aplicável, o fluxo deve terminar rápido;
  • o core não deve depender de múltiplos observers espalhados como publish principal.

O que evitar

<?php

namespace local_middag\framework\application\service\dashboard;

final class dashboard_service
{
    public function count_enrolments(int $user_id): int
    {
        global $DB;

        return $DB->count_records('user_enrolments', ['userid' => $user_id]);
    }
}

No framework, isso espalha dependência do Moodle para fora da fronteira correta. Em extensions, isso deve ser exceção. Em plugins terceiros, isso é livre, mas não é caminho protegido pelo MIDDAG.