O mapeamento é objeto-relacional, não é relacional-objeto.

domingo, 17 de janeiro de 2010


É isso aí, deveria ter uma seta no lugar deste hífen:
objeto->relacional

Infelizmente, as pessoas insistem em chamar certo, mas fazer assim:

relacional->objeto

Eu entendo que algumas pessoas trabalharam usando MER e DFD e matriz de interações (matriz CRUD) durante muitos anos e acham que deviam continuar usando primariamente estas ferramentas no desenvolvimento de sistemas orientados a objetos, muitas vezes em detrimento de outras mais ricas como modelos UML.
<Este post é longo, mas o final surpreende.>

Não estou defendendo que se use modelos UML, o que estou querendo discutir aqui é a implicação de se usar uma ferramenta na hora errada.

Antes eu gostaria de discutir a questão da diferença entre o modelo orientado a objetos e o modelo relacional.

O modelo relacional se presta a armazenar dados em tabelas, gerando relações. O modelo relacional não trata de comportamento, não trata de entidades do mundo real, não trata de relacionamentos entre estas entidades. Trata de dados em tabelas e relações matemáticas. Daí a necessidade de se fazer um mapeamento.

Apesar de se falar em entidades e relacionamento num modelo relacional ("Modelo de Entidades e Relacionamentos"), ele trata de tabelas e as relações entre elas. Estas tabelas são tabelas com linhas, chamadas de registros e colunas que contém dados com extremas limitações.

Por exemplo, não se pode associar um registro a vários outros utilizando uma única coluna pois numa coluna só se coloca UM valor.

Logo, se queremos fazer uma associação 1:N ("um para n" ou "um para muitos"):



Para associar um registro Aluno (com um identificador) a outros de outra tabela, por exemplo, Matrícula (supondo que um aluno possa ter uma matrícula para cada curso na mesma instituição), eis a solução:



Neste caso, cada matrícula só pode se relacionar com um aluno. Se fosse possível relacionar uma matrícula com mais de um aluno, ou teríamos vários registros com IDs diferentes e o mesmo número de matrícula, para ter diferentes valores na coluna id_aluno (o que poderia causar várias inconsistências ao alterar uma e esquecer de alterar as outras) ou teríamos que adotar outra solução. Como isto é exatamente o caso de Aluno e Turma, o pior ainda está por vir, o relacionamento n:m ("n para m" ou "muitos para muitos"):



Digo "pior" porque esta solução cria uma entidade de relacionamento, uma entidade fraca, que possivelmente não existe no mundo real, mas existe no modelo relacional por uma restrição tecnológica. Os dados ficariam assim armazenados:




O problema da modelagem relacional é que estas restrições são impostas pela maneira matemática de armazenar e trabalhar com estas informações, através do uso de álgebra relacional.

Como os sistemas gerenciadores de bancos relacionais são muito estáveis, bem estabelecidos no mercado, extremamente confiáveis e extremamente rápidos graças ao desenvolvimento da álgebra relacional e anos de otimizações nas implementações, eles se mantém no mercado mesmo quando o sistema é desenvolvido utilizando orientação a objetos.

Acontece que como os objetos não têm estas restrições, o modelo OO é mais simples, mais próximo do real e isto exige que seja feito um mapeamento dos atributos do objeto para as colunas das tabelas.
Isto seria simples se:

  1. não houvesse herança, que não tem correspondente no modelo relacional. Existem 3 estratégias principais para se mapear heranças (single table, table per class e joined subclasses). Como o foco aqui não é explicar o mapeamento O-R, com estratégias já bem conhecidas, vão ao Google procurar páginas como esta.
  2. se não houvesse encapsulamento e níveis de visibilidade diferentes para atributos e classes, pois não há correspondência no modelo relacional.
  3. se relacionamentos não tivessem "navegabilidade".Ou seja, se Aluno tem um atributo Matrícula mas Matrícula não tem atributo para Aluno, o relacionamento é unidirecional e não tem correspondência no modelo relacional.
  4. se não houvesse atributos que na verdade são coleções de outros objetos, pois uma coluna (para aquele atributo) não pode guardar uma quantidade indeterminada de valores. Apenas um. E se for assim nas duas pontas do relacionamento, teremos uma tabela de relacionamento que não representa classe alguma.
Felizmente, hoje em dia, é possível fornecer metadados (muitas vezes em XML ou Anotações em Java) para as nossas classes de forma que ferramentas automáticas se encarreguem de construir o banco conforme as nossas instruções nos metadados de forma adequada.

Outra alternativa é construir o banco de forma a comportar as nossas classes manualmente. Como este trabalho é muito custoso, normalmente se faz uso de uma ferramenta.

Quando se faz uso do MER antes de se ter o diagram de classes, o que acontece é que o banco é criado e as classes passam a espelhar o banco. Ou cria-se as classes manualmente, copiando-as do banco, uma classe para cada tabela ou se faz uso de ferramentas automáticas para isto.

Neste momento, perde-se todas as heranças (joined subclasses viram associações simples, table per class aglutina classes abstratas na próxima superclasse concreta e single table junta todas as classes a uma classe só), surgem tabelas que não existem como classes com nomes esquisitos, perde-se a navegabilidade dos relacionamentos (muitas vezes impactando no desempenho geral do sistema e aumentando o acoplamento, criando dependências circulares) e perde-se informação de visibilidade dos atributos que muitas vezes acabam trazendo nomes estranhos e fora do padrão de codificação da linguagem adotada.

Tem-se um modelo totalmente deformado. A manutenção fica prejudicada, a compreensão do código fica prejudicada, o desempenho fica prejudicado e a própria codificação fica prejudicada, gerando muitas vezes código extra, com verificações, comparações e loops desnecessários.

Claro que já peguei código assim. Mas vou lhes contar a vez mais estarrecedora:

Imaginem a minha surpresa quando encontrei um banco onde todos os relacionamentos 1:n foram promovidos a n:m sob a desculpa de que um dia o relacionamento poderia ter que ser modificado, então já se modificou todos antes, indiscriminadamente. Mais ou menos como se você fosse para a frente de uma garagem de hospital se jogar na frente de uma ambulância em baixa velocidade toda vez que você fosse atravessar uma rua para minimizar o tamanho do possível acidente.

A questão a ambulância não estava em baixa velocidade. Vinha em alta velocidade, com a sirene ligada e um paciente em estado crítico sendo transportado. Não só o modelo tinha cerca de 770 classes como cerca de 425 (mais da metade) eram de relacionamento já que todos os relacionamentos que não eram 1:1 eram n:m. Pra piorar, todo registro de uma tabela de relacionamento entre A e B tinha um número que significava a posição do objeto B no atributo (coleção) em A, para que ficassem na ordem, mesmo que a ordem não importasse para absolutamente nada para a aplicação e para o usuário e sua supressão não fizesse a menor diferença.

Depois de meses de conversa e persuasão, a ordem foi abolida onde não era importante e o mapeamento foi construído de forma a deixar as tabelas de relacionamento transparentes onde fosse possível. Aproximadamente 410 classes de relacionamento foram retiradas do sistema, sobrando algo em torno de 15. Infelizmente esta era só uma das idiossincrasias da modelagem daquele banco.

E você? Tem alguma história semelhante para contar?

Abraço a todos!

Bookmark and Share

Nenhum comentário:

Postar um comentário

 
addthis_config = { data_ga_tracker: pageTracker }