Design patterns para a camada de banco de dados
Índice
Status
Em andamento/rascunho.
Visão geral
Da Wikipedia:
"Design patterns (padrões de projeto) descrevem soluções para problemas recorrentes no desenvolvimento de sistemas de software orientados a objetos." ( link direto)
Os design patterns podem ajudar a diminuir o problema arquitetural das camadas do i-Educar. Várias classes no i-Educar padecem do problema da duplicação de código e as classes mais afetadas são aquelas que lidam com a persistência dos dados (são as classes que estão no diretório intranet/include/*/.
Vejamos o exemplo das classes clsEscolaridade e clsPmieducarInstituicao.
Ambas as classes apresentam métodos semelhantes: cadastra(), lista(args...), edita() e exclui() (métodos CRUD). Mas também existem métodos diferentes, embora o objetivo destes sejam de implementar o mesmo comportamento. Em clsPmieducarInstituicao, o método setLimite(int, int) define o limite de registros retornados pelo método lista(args...). Já em clsEscolaridade, essa lógica está dentro do método lista(args...), que recebe os parâmetros $int_limite_ini e $int_limite_qtd.
Essa redundância e falta de coesão nas classes são frutos de um design ruim. Para piorar, essas classes que aparentam ser classes de lógica de domínio (ou regras de negócio) não passam de Gateways para as tabelas do banco de dados. O ruim dessa aproximação é a maioria da lógica de domínio está enterrada nas classes que manipulação a lógica da aplicação e a interface web.
Um dos problemas nas classes atuais são os construtores, uns chegam a ter mais de uma dúzia de argumentos. Isso faz com que a aplicação de desenvolvimento orientado a testes mais dispendiosa. Uma falta de atenção e você acaba passando um valor numérico para um argumento que precisa receber uma string e vice-versa. Maior que isso somente o fato de cada classe dessa camada instanciar por conta própria um objeto clsBanco(). Isso dificulta a instanciação da classe para uso em testes unitários, além de degradação de performance na execução dos mesmos, já que não há uma forma de desabilitar a execução das queries SQL. Testes usando a classe PHPUnit_Extensions_Database_TestCase torna-se impraticável sem o uso de um objeto de conexão PDO (nesse caso, seria o PDO pgsql).
A aplicação de design patterns na refatoração dessas classes pode diminuir sensivelmente essa duplicação de código, tornar o código mais testável e melhorar a organização das camadas da aplicação. Essa proposta visa levantar a discussão para a implementação de patterns para a camada de banco de dados, motivado pelos itens supra-citados.
Operação
Existe uma variedade de design patterns interessantes para se analisar. Os mais conhecidos são os patterns que Fowler explana em seu livro [Patterns of Enterprise Application Architecture] (PofEAA). São patterns bem conhecidos e largamente implementados em diversos frameworks.
Todos os design patterns tem suas vantagens e desvantagens. O objetivo aqui é o de selecionar um que melhor se adapta a realidade do i-Educar, considerando o tamanho da aplicação e a complexidade.
Os design patterns arquiteturais de Fowler são:
Active Record talvez seja um dos patterns mais comentados, principalmente por ser a implementação de persistência padrão do framework Ruby on Rails. É um pattern simples, que contém métodos de acesso ao banco de dados e de lógica de domínio ao mesmo tempo, o que torna o seu uso bastante intuitivo. Em PHP, o ORM Doctrine é a implementação mais conhecida do Active Record.
Table Data Gateway contém apenas métodos de acesso ao banco de dados. Outros objetos usam esse gateway para persistirem os dados (CRUD). Lógica de domínio não faz parte do comportamento de um Table Data Gateway. Uma instância desse pattern mantém acesso a todos os registros da tabela mapeada pela classe. O componente Zend_Db_Table do Zend Framework implementa esse pattern.
Row Data Gateway é semelhante ao Table Data Gateway. A diferença é que, enquanto o Table Data Gateway mantém todos os registros da tabela em uma única instância, o Row Data Gateway contém uma instância por registro da tabela de banco de dados. O componente Zend_Db_Table_Row implementa esse pattern.
Data Mapper é o mais complexo dos patterns. Este implementa uma camada adicional na aplicação, separando completamente o acesso ao banco de dados do restante da aplicação. Seu objetivo é de transferir dados entre o banco de dados para os objetos de domínio. Doctrine implementa o Data Mapper junto com o Active Record. No tutorial de exemplo do Zend Framework, uma aplicação do Data Mapper é exemplificada (apesar de não existir um componente pronto para extender).
Objetivo
Apesar de diversos frameworks e bibliotecas disponibilizarem esses padrões de uma forma bem implementada, o objetivo é o de criar uma implementação enxuta com o mínimo de funcionalidades necessárias. Caso o uso de uma biblioteca externa seja considerado, classes wrapper dentro do namespace CoreExt deverão ser criadas para garantir que uma mudança na API externa resulte em erros de execução nas classes do i-Educar.
