Esses dias me peguei pensando em um costume interessante que temos. Quando digo "nós", falo de min e das pessoas com as quais já trabalhei nesses 4 anos que atuo com .NET. Fomos acostumados, em se tratando de persistência em banco de dados, de sempre utilizar modelo relacional pra qualquer tipo de armazenamento de objeto. O problema é que nem sempre isso é necessário e acabamos perdendo ou tempo em codificação ou desempenho ou espaço em disco.
A idéia de banco de dados orientado a objetos sempre me agradou e acho uma pena não ter chegado ao mainstream. No entanto o conceito de armazenar o grafo todo de objetos é muito interessante em algumas situações. Vou usar um exemplo que talvez não seja o melhor, mas deve passar a idéia.
Imagine um cadastro de cliente com diversas entidades relacionadas: diversos endereços, vários telefones para contato, dependentes, etc.
Normalmente criaríamos uma tabela para cada uma das entidades e essas tabelas teriam uma referência (FK) para a tabela principal de clientes:
Se considerássemos que um endereço ou automóvel pudesse pertencer a mais de um cliente a coisa ficaria ainda mais complicada e teríamos que criar tabelas de relacionamento N:M.
Para que o exemplo seja útil, vamos considerar que nesse sistema não fosse possível efetuar pesquisas utilizando como filtro nenhuma dessas entidades (em um sistema real é óbvio que requisito é um absurdo) e só seria disponibilizada uma funcionalidade de busca por meio de código (id). Mesmo com esse requisito, como pensamos sempre em de forma relacional o caminho natural seria criar o modelo de dados acima e armazenar tudo bastante normalizado.
No entanto existe outra opção que em determinados casos é muito interessante. Guardar o objeto inteiro no banco.
Em .NET, por exemplo, bastaria criar um modelo simples de dados contendo o idCliente e um campo que armazene binário (varbinary em SQL), serializar o objeto e pronto.
public Cliente Consultar(int codigoCliente)
{
//Recupera do DB
byte[] clienteSerializado = DaoCliente.ConsultarPorCodigo(codigoCliente);
//Devolve o objeto deserializado
return this.DeserializarObjeto<Cliente>(clienteSerializado);
}
public void Salvar(Cliente novoCliente)
{
//Serializa o objeto
byte[] clienteSerializado = this.SerializarObjeto<Cliente>(novoCliente);
//Guarda o cliente no DB
DaoCliente.Inserir(novoCliente.Codigo, clienteSerializado);
}
private byte[] SerializarObjeto<T>(T objeto)
{
MemoryStream ms = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(ms, objeto);
byte[] objetoSerializado = ms.ToArray();
ms.Close();
return objetoSerializado;
}
private T DeserializarObjeto<T>(byte[] objetoSerializado)
{
MemoryStream ms = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
ms.Write(objetoSerializado, 0, objetoSerializado.Length);
ms.Position = 0;
return (T)formatter.Deserialize(ms);
}
Dessa maneira, sempre que precisarmos, já iremos ter o objeto completo e não seria necessário executar diversas instruções SQL ou stored procedures e muitas chamadas ao banco. Iriamos apenas executar uma chamada que nos retornaria o objeto completo e pronto para uso.
Essa é uma solução diferente e que pode ser útil em alguns cenários, principalmente se você não utiliza nenhum framework ORM. Contudo, é importante verificar todos os requisitos com bastante cuidado, com essa solução corremos o risco de ficar com dados desatualizados e perdemos a possibilidade de efetuar buscas por campos que estejam dentro dos objetos armazenados.
Acho muito legal encontrarmos formas diferentes de fazer as mesmas coisas que estamos habituados. É uma maneira muito eficiente de aprendizado e conhecimento das nossas ferramentas.
O que você acha?
Editado: sem saber, acabei descrevendo o pattern Serialized Lob já catalogo pelo Fowler em seu livro PoEAA. O Bruno achou e comentou o post colocando este link. Valeu! :D