Pular para conteúdo

Modelo de Persistência

O MIDDAG adota um modelo de persistência orientado por famílias arquiteturais, com repository como fronteira oficial de acesso a dados.

Esse desenho existe para sustentar flexibilidade de produto sem espalhar schema, SQL e detalhes de hidratação pelo restante do framework.

Premissa estrutural

No local_middag, a base de persistência do produto é fixa. O framework e as extensions internas devem operar sobre as famílias oficiais do schema, e não criar novas tabelas de domínio para cada nova capacidade.

Se essa premissa se mostrar inviável no futuro, isso deve ser tratado como mudança profunda de arquitetura e exigirá novo ADR.

A unidade central: item

item é a unidade canônica de persistência flexível do MIDDAG.

Ele cumpre papel equivalente ao post no ecossistema WordPress: um registro-base tipado, capaz de representar diferentes objetos de negócio sem exigir uma nova tabela para cada capacidade do produto.

No MIDDAG:

  • TYPE define o tipo lógico do registro;
  • a tabela principal guarda atributos estruturais recorrentes;
  • a tabela de metadata absorve extensibilidade útil;
  • o model hidratado define o comportamento de domínio correspondente.

Essa abordagem já foi validada em ecossistemas amplos como WordPress e plugins como WooCommerce. O objetivo aqui não é copiar WordPress literalmente, e sim aplicar a mesma vantagem estrutural: flexibilidade com camada estável de persistência.

As quatro famílias de persistência

1. Current state

  • middag_items
  • middag_itemmeta

Representam o estado operacional atual do framework.

Use essa família quando o objetivo for:

  • persistir o estado corrente de uma entidade do produto;
  • suportar múltiplos tipos de item no mesmo modelo base;
  • permitir extensibilidade sem migração frequente.

2. Revision history

  • middag_item_revision
  • middag_item_revision_meta

Representam histórico imutável de um item.

Use essa família quando o objetivo for:

  • registrar versões sucessivas de um item;
  • reconstruir estado anterior;
  • preservar histórico estrutural separado do estado atual.

3. Audit trail

  • middag_audit_log
  • middag_audit_diff
  • middag_audit_snapshot

Representam fatos auditáveis derivados de signals e outras ocorrências despachadas pelo framework.

Use essa família quando o objetivo for:

  • rastrear o que aconteceu;
  • registrar contexto, ator e origem;
  • guardar diff ou snapshot observável de mudanças.

4. Job governance

  • middag_job
  • middag_job_attempt

Representam a governança persistida de trabalho assíncrono e observável.

Use essa família quando o objetivo for:

  • registrar intenção e status de execução;
  • controlar tentativas e deduplicação;
  • manter rastreabilidade operacional sem substituir o runtime do Moodle.

O schema oficial atual

Estas são as tabelas oficiais do produto no estado arquitetural atual:

  • middag_items
  • middag_itemmeta
  • middag_item_revision
  • middag_item_revision_meta
  • middag_audit_log
  • middag_audit_diff
  • middag_audit_snapshot
  • middag_job
  • middag_job_attempt

Tabelas invioláveis

Dentro do local_middag, esse conjunto é tratado como base fixa da arquitetura atual. Core e extensions internas não devem introduzir novas tabelas de domínio locais como alternativa casual ao modelo. Se isso precisar mudar, será uma mudança profunda e exigirá novo ADR.

Modelo híbrido

O MIDDAG usa um modelo híbrido:

  • colunas reais para dados estruturais recorrentes;
  • metadata para extensibilidade;
  • repositories para esconder joins, hidratação, serialização e transações.

Isso evita dois extremos ruins:

  • criar tabela nova para cada tipo de item;
  • jogar toda a modelagem em metadata e perder estrutura.

Como cada camada deve aplicar

Os exemplos abaixo são educacionais. Eles mostram a direção arquitetural esperada.

Framework

O framework trabalha sobre repositories e tipos de item, sem vazar nome de tabela.

<?php

namespace local_middag\framework\application\service\catalog;

use local_middag\framework\contract\repository\item_repository_interface;

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

    public function find_item(int $item_id): object|null
    {
        return $this->item_repository->find_by_id($item_id);
    }
}

Extension do ecossistema

A extension reutiliza o modelo item e pode especializar comportamento por TYPE, sem criar tabela própria dentro do local_middag.

<?php

namespace local_middag\extensions\ecommerce\service;

use local_middag\framework\contract\repository\item_repository_interface;

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

    public function find_store(int $item_id): object|null
    {
        return $this->item_repository->find_by_id($item_id);
    }
}

Direção esperada

Mesmo quando a extension representa um conceito próprio, como loja, pedido ou catálogo, a direção é modelar isso sobre item e famílias complementares do framework, não abrir nova tabela da extension.

Plugin terceiro

O plugin terceiro pode escolher entre aderir ao modelo do MIDDAG ou manter schema próprio.

Aderindo ao modelo do MIDDAG

<?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);

Mantendo tabela própria

<?php

global $DB;

$records = $DB->get_records('local_partner_orders', ['status' => 'pending']);

Liberdade de terceiros

Plugins terceiros podem ter tabelas próprias, mas essa liberdade não altera a regra do ecossistema interno do local_middag. O core e as extensions internas continuam presos às famílias oficiais do framework.

O que não fazer

  • Não tratar nomes de tabela como API pública do framework.
  • Não acessar tabelas do core diretamente em services, controllers ou extensions.
  • Não criar novas tabelas de domínio dentro do core ou das extensions internas do local_middag.
  • Não usar metadata para esconder campos essenciais que deveriam ser estruturais.

Quando algo não couber no modelo

Se uma necessidade importante não couber bem em item e nas famílias complementares, isso não deve virar escolha local de implementação.

O caminho correto é:

  1. reconhecer a limitação como questão arquitetural;
  2. validar se o problema é real e recorrente;
  3. tratar a mudança como decisão explícita em novo ADR.