Pular para o conteúdo

ADR-002 - Abstração de Banco de Dados

Registro de Decisão de Arquitetura para padrão de acesso ao banco de dados orientado a objetos do XOOPS.


Aceito - Padrão principal desde XOOPS 2.0


XOOPS precisava de uma estratégia de interação com banco de dados que:

  1. Abstraísse sintaxe SQL específica do banco de dados
  2. Fornecesse operações CRUD consistentes em todos os módulos
  3. Permitisse desinfecção automática de dados e escape
  4. Suportasse futuras mudanças de mecanismo de banco de dados
  5. Simplificasse operações comuns para desenvolvedores

As alternativas eram:

  • SQL bruto em toda a base de código
  • ORM completo (Doctrine, Eloquent)
  • Abstração customizada leve

graph TB
subgraph "Camada de Aplicação"
A[Código de Módulo]
B[Métodos de Manipulador]
end
subgraph "Camada de Abstração"
C[XoopsObjectHandler]
D[XoopsPersistableObjectHandler]
E[Sistema de Critérios]
end
subgraph "Camada de Banco de Dados"
F[XoopsDatabase]
G[MySQLDatabase]
H[Wrapper PDO]
end
subgraph "Armazenamento"
I[(MySQL/MariaDB)]
end
A --> B
B --> C
B --> D
C --> E
D --> E
E --> F
F --> G
F --> H
G --> I
H --> I

Implementaremos um Padrão de Manipulador com:

Cada entidade de dados estende XoopsObject:

class Item extends XoopsObject
{
public function __construct()
{
$this->initVar('id', XOBJ_DTYPE_INT, null, false);
$this->initVar('title', XOBJ_DTYPE_TXTBOX, '', true, 255);
$this->initVar('content', XOBJ_DTYPE_TXTAREA, '', false);
$this->initVar('status', XOBJ_DTYPE_INT, 0, false);
}
}

Cada objeto possui um manipulador correspondente:

class ItemHandler extends XoopsPersistableObjectHandler
{
public function __construct($db)
{
parent::__construct($db, 'mymodule_items', Item::class, 'id', 'title');
}
// Métodos CRUD herdados:
// - create(), get(), insert(), delete()
// - getObjects(), getCount(), getAll()
}

Condições de consulta orientadas a objetos:

$criteria = new CriteriaCompo();
$criteria->add(new Criteria('status', 1));
$criteria->add(new Criteria('created', time() - 86400, '>='));
$criteria->setSort('created');
$criteria->setOrder('DESC');
$criteria->setLimit(10);
$items = $handler->getObjects($criteria);

// Tipos de variáveis com sanitização automática
XOBJ_DTYPE_INT // Inteiro
XOBJ_DTYPE_TXTBOX // Texto de uma linha (escapado)
XOBJ_DTYPE_TXTAREA // Texto de múltiplas linhas (escapado)
XOBJ_DTYPE_EMAIL // Validação de email
XOBJ_DTYPE_URL // Validação de URL
XOBJ_DTYPE_ARRAY // Array serializado
XOBJ_DTYPE_OTHER // Sem processamento
XOBJ_DTYPE_FLOAT // Ponto flutuante

classDiagram
class XoopsObjectHandler {
<<abstract>>
#db: XoopsDatabase
+create(): XoopsObject
+get(id): XoopsObject
+insert(object): bool
+delete(object): bool
}
class XoopsPersistableObjectHandler {
+getObjects(criteria): array
+getCount(criteria): int
+getAll(criteria): array
+deleteAll(criteria): bool
+updateAll(field, value, criteria): bool
}
class ItemHandler {
+getPublishedItems(): array
+getByCategory(catId): array
}
XoopsObjectHandler <|-- XoopsPersistableObjectHandler
XoopsPersistableObjectHandler <|-- ItemHandler

  1. Consistência: Todos os módulos usam os mesmos padrões
  2. Segurança: Escape automático previne injeção SQL
  3. Simplicidade: Operações comuns requerem código mínimo
  4. Manutenibilidade: Mudanças na camada de banco de dados não afetam módulos
  5. Testabilidade: Manipuladores podem ser simulados para testes
  1. Performance: Overhead de abstração extra
  2. Complexidade: Curva de aprendizado para novos desenvolvedores
  3. Limitações: Consultas complexas podem precisar de SQL bruto
  4. Problema N+1: Sem carregamento aprimorado integrado
  • Performance: Cache de objetos acessados frequentemente
  • Consultas complexas: Permitir SQL bruto quando necessário
  • N+1: Use getAll() com critérios apropriados

flowchart LR
subgraph "Atual (2.5.x)"
A[XoopsDatabase]
B[Handlers]
C[Criteria]
end
subgraph "Futuro (4.0.x)"
D[PDO/DBAL]
E[Padrão de Repositório]
F[Construtor de Consultas]
G[Contêiner DI]
end
A --> D
B --> E
C --> F
D --> G
E --> G

Planos do XOOPS 4.0:

  • Doctrine DBAL para abstração de banco de dados
  • Padrão de repositório substituindo manipuladores
  • Construtor de consultas para consultas complexas
  • Integração completa de contêiner PSR-11

$helper = Helper::getInstance();
$handler = $helper->getHandler('Item');
// Criar
$item = $handler->create();
$item->setVar('title', 'Novo Item');
$handler->insert($item);
// Ler
$item = $handler->get($id);
$title = $item->getVar('title');
// Atualizar
$item->setVar('title', 'Título Atualizado');
$handler->insert($item);
// Deletar
$handler->delete($item);
$criteria = new CriteriaCompo();
$criteria->add(new Criteria('status', 'published'));
$criteria->add(new Criteria('category_id', '(1,2,3)', 'IN'));
$criteria->add(new Criteria('created', strtotime('-30 days'), '>='));
$criteria->setSort('views');
$criteria->setOrder('DESC');
$criteria->setLimit(10);
$criteria->setStart(0);
$items = $handler->getObjects($criteria);
$total = $handler->getCount($criteria);

  • ADR-001: Arquitetura Modular
  • ADR-003: Motor de Template Smarty

  • Martin Fowler - Patterns of Enterprise Application Architecture
  • Conceitos de Domain-Driven Design
  • Padrões Active Record vs Data Mapper

#xoops #architecture #adr #database #handler #design-decision