Pular para conteúdo

Visão Geral da Arquitetura

O MIDDAG adota uma arquitetura inspirada em conceitos de DDD (Domain-Driven Design) e Arquitetura Hexagonal (Ports and Adapters), adaptada para conviver dentro do ecossistema procedural do Moodle.

O objetivo principal é desacoplar a lógica de negócio (Regras Educacionais, Gestão de Itens, Extensões) da infraestrutura do Moodle (Banco de Dados global, Globals $DB, $CFG, Output Buffers).


Fluxo de Requisição (Request Lifecycle)

Diferente de plugins tradicionais que misturam HTML, SQL e lógica em arquivos .php soltos, o MIDDAG centraliza o fluxo de execução através de um Kernel.

O diagrama abaixo ilustra como uma requisição sai do Moodle e trafega pelas camadas do plugin até retornar uma resposta:

graph TD
    subgraph Moodle_Environment [Ambiente Moodle]
        User((Usuário)) -->|Requisição HTTP| MoodleCore[Moodle Core / Lib.php]
        MoodleCore -->|Hook / Entry Point| Facade[Facade Estática: middag::class]
    end

    subgraph MIDDAG_Core [Camada de Aplicação]
        Facade -->|init & handle| Kernel[Kernel]
        Kernel -->|Boot| Container[DI Container]
        Kernel -->|Match Route| Router[Route Manager]

        Router -->|Dispatch| Controller[Base Controller]

        Controller -->|Call| Service[Service Layer]
    end

    subgraph Domain_Layer [Camada de Domínio]
        Service -->|Orchestrate| DomainEntity["Domain Entity (Item)"]
        Service -->|Use| QueryBuilder[Query Builder]
    end

    subgraph Infrastructure_Layer [Camada de Infraestrutura]
        QueryBuilder -->|Build SQL| Repository[Repository]
        Repository -->|Hydrate| Mapper[Data Mapper]
        Repository -->|Execute SQL| MoodleDB[(Moodle Database)]
    end

    Repository -->|Return Entity| Service
    Service -->|Return DTO/Result| Controller
    Controller -->|Return Response| MoodleCore

Camadas da Aplicação

A arquitetura é dividida em 4 camadas estritas. Uma camada superior pode depender das inferiores, mas o inverso é proibido (ex: o Domínio nunca deve saber sobre o Controller).

1. Camada de Interface (Platform)

Responsável por receber a entrada do usuário (HTTP, CLI, Ajax) e converte-la para comandos que a aplicação entende.

  • Controllers: Recebem Request, validam permissões (Auth) e delegam para Services.
  • API: Endpoints REST/Ajax que retornam JSON estrito.
  • Console/CLI: Comandos de terminal para manutenção.

2. Camada de Aplicação (Service)

É a "cola" do sistema. Contém a lógica de orquestração, mas não a lógica de negócio pura.

  • Services: Coordenam operações (ex: "Criar um item e notificar o usuário").
  • Extension Service: Gerencia o ciclo de vida dos módulos adicionais.

3. Camada de Domínio (Core Domain)

O coração do software. Aqui residem as regras de negócio, invariantes e a representação dos dados. Esta camada ignora a existência de banco de dados ou HTML.

  • Entities: Objetos com identidade única (ex: item). São imutáveis ou controlados.
  • Value Objects: Objetos definidos por seus atributos (ex: pagination, sorting).
  • Registry: Mapeamento de tipos e relações.

4. Camada de Infraestrutura (Infrastructure)

Implementa os detalhes técnicos para persistir e recuperar dados.

  • Repositories: Traduzem comandos de domínio para SQL do Moodle.
  • Mappers: Convertem stdClass (banco) para Entity (domínio).
  • Query Engine: Construtor fluente de SQL complexo.

Princípios de Engenharia

Para manter a sanidade do código em um projeto desta escala, seguimos regras rígidas:

Imutabilidade (Immutability)

Objetos de Domínio e Query Builders são imutáveis. Métodos que alteram estado retornam novas instâncias (Wither Pattern).

  • Por que? Previne efeitos colaterais indesejados onde um serviço altera um objeto que está sendo usado por outro.

Fail-Fast

Não silenciamos erros. Se uma classe é instanciada incorretamente ou um serviço não existe, lançamos uma coding_exception ou RuntimeException imediatamente.

  • Por que? Erros silenciosos no Moodle (ex: null returns) causam bugs difíceis de rastrear em produção.

Injeção de Dependência (DI)

Não usamos new Class() dentro de serviços. Todas as dependências são injetadas via construtor e gerenciadas pelo ContainerBuilder (Symfony DI).

  • Por que? Facilita testes unitários (Mocking) e troca de implementações.

Segregação de Interfaces (ISP)

Preferimos interfaces pequenas e específicas (extension_interface, event_service_interface) a classes gigantescas.


Padrões de Projeto Utilizados

Padrão Onde é usado Objetivo
Singleton Kernel, middag (Facade) Garantir ponto único de entrada e boot.
Facade middag::class Simplificar o acesso à API complexa do Kernel para o dev Moodle.
Builder Query, DiffBuilder Construir objetos complexos passo-a-passo de forma fluente.
Repository item_repository Abstrair a camada de dados (SQL).
Data Mapper item_mapper Converter dados relacionais em objetos.
Strategy Extensions Permitir que comportamentos variados sejam plugados no Core.