DORM – Delphi ORM
Há pouco tempo tomei conhecimento que o projeto DORM [1] (Delphi ORM), de autoria do Daniele Teti [2] se tornou público, rapidamente baixei o projeto para poder experimentar, e aqui vão minhas opiniões.
Não pretendo entrar em questões como o que é um ORM, sua utilidade, etc.
De cara o que me chamou atenção no DORM é que a proposta do framework é que ele não interfere na modelagem dos seus objetos. Ou seja, seus objetos de negócio não precisam herdar/implementar nenhuma classe/interface em especial, os atributos não precisam ser published, os atributos são de tipos primitivos da linguagem, etc. Em resumo, uma classe como a abaixo pode ser persistida sem problemas:
type
TCliente = class
private
FId: Integer;
FNome: String;
public
property Id: Integer read FId write FId;
property Nome: String read FNome write FNome;
end;
Um outro ponto positivo que encontrei é que ele faz uma clara distinção entre bases de dados para desenvolvimento, testes e produção, sendo que você especifica isso no momento de criar a sessão. Normalmente é simples selecionar bancos diferentes para cada situação (desenvolvimento/testes/produção), então o DORM não trouxe nada de outro mundo, mas o fato de isto já vir embutido no framework é algo muito positivo. Digo isso não por poupar o desenvolvedor de algum trabalho extra, mas por incentiva-lo a trabalhar desta forma.
Uma coisa que senti falta é mapeamento por CoC. Ou seja, para o exemplo que eu dei acima, seria interessante que, sem eu ter fazer nenhum mapeamento, o framework já assumisse que a tabela se chama CLIENTE, e os campos ID e NOME. E para casos em que se deseja sair do padrão, aí sim eu poderia recorrer ao mapeamento manual.
O mapeamento, bem como a configuração de conexão com os bancos, é feita em um arquivo externo em formato JSON. Não investiguei mais a fundo, mas me parece ser possível montar a configuração em memória (montando uma string em JSON) e utilizar isso ao criar a sessão, assim não seria necessario manter um arquivo externo com estes dados.
O mapeamento não é documentado, mas olhando os exemplos que acompanham o projeto foi facil ver como eu devia proceder com as minhas classes.
Alias, documentação é um ponto fraco do projeto, mas não me preocupo muito com isso. Não é que documentação não seja importante, mas uma boa suíte de testes automatizados já quebram um galho quando eu preciso aprender alguma coisa sobre o uso do framework, e de qualquer forma eu prefiro que o autor gaste seu tempo escrevendo código do que documentos
Outra coisa que seria legal de ter é mapeamento por atributos. Seria muito interessante se fosse possível mapear as classes com algo mais ou menos assim:
type
[Table('CLIENTE')]
TCliente = class
private
[PrimaryKey]
[Column('ID')]
FId: Integer;
[Column('NOME')]
FNome: String;
public
property Id: Integer read FId write FId;
property Nome: String read FNome write FNome;
end;
Isso, aliado ao CoC, reduziria bastante o esforço para mapear as classes.
O projeto conta com uma aparentemente razoavel cobertura de testes automatizados, o que sem dúvida é um ponto importante, pois mostra a maturidade do autor, e nos dá segurança para confiar no código.
Apesar de eu mesmo ainda não ter usado, sei que o projeto suporta associações, inclusive de coleções. A respeito de coleções, a versão no trunk exige o uso da classe TdormCollection para coleções, mas já existe um branch em desenvolvimento para permitir o uso de qualquer coleção através de duck-typing [3], ou seja, qualquer objeto que possua métodos como Add() e afins, poderá ser utilizado.
Lazy loading também é suportado, mas não de forma transparente como em frameworks mais modernos como por exemplo o Hibernate. Existem uma série de limitações que dificultam/impossibilitam a implementação de lazy loading de forma transparente no Delphi, mas isso não vem ao caso.
Criei algumas entradas no issue tracker do projeto a respeito de alguns dos pontos levantados aqui neste post. Para quem tiver interesse, sugiro ir lá e sinalizar o issue ou mesmo contribuir com algum patch.
- http://code.google.com/p/delphi-orm/issues/detail?id=5 Convention over Configuration (CoC)
- http://code.google.com/p/delphi-orm/issues/detail?id=6 Mapeamento por atributos
- http://code.google.com/p/delphi-orm/issues/detail?id=8 Travamento otimista através de versionamento de registros
Estou tentando contato com o autor há alguns dias, mas ele não me responde nem por e-mail nem nos issues que lancei no projeto. Eu gostaria de contribuir implementando algumas das solicitações que eu lancei, mas antes preciso saber se o autor concorda com as mesmas e aceitaria contribuições de terceiros. Havendo qualquer retorno, eu atualizo o post aqui.
Update – 23/11/2011
Recebi alguns retornos do Daniele Teti, ele aceitou minhas sugestões e me colocou como commiter para que eu possa ir trabalhando nelas, enquanto ele se foca no core do framework.
Infelizmente meu tempo é limitado, já que o Emballo [4] toma boa parte dele, mas dentro das possibilidades pretendo dar minha contribuição ao projeto. Se alguem mais quiser contribuir, é só se manifestar lá no site do projeto.
[1] http://code.google.com/p/delphi-orm/
[2] http://www.danieleteti.it/
[3] http://en.wikipedia.org/wiki/Duck_typing
[4] http://code.google.com/p/emballo


Olá Magno, vi que você perguntou sobre o TMS Aurelius no seu Twitter. É uma framework ORM da TMS Software. Aqui vai um link sobre ele: http://www.tmssoftware.com/site/blog.asp?post=208. Você estará na Delphi conference?
Wagner… Bem que eu imaginei, rsrs, mas na época não consegui encontrar nada nem no site da TMS (não devo ter procurado direito), daí a dúvida.
Infelizmente não vou estar no Delphi Conference
Pode indicar algum local onde consigo mais informações sobre este framework?
Você tem relação com o seu desenvolvimento?
Magno, sim, o TMS Aurelius está totalmente sob minha responsabilidade. Podemos trocar mais detalhes por e-mail, mande um e-mail pra TMS perguntando do Aurelius, o e-mail virá pra mim e aí conversamos, pode ser?
Algum problema específico referente a implementação de lazy loading no Delphi?
Problema nenhum, tanto que o DORM mesmo implementa.
A dificuldade é implementar de forma que fique transparente, ou seja, você declara algo assim:
type TCustomer = class public property City: TCity read FCity write FCity; end;E o objeto TCity associado só vai ser carregado do banco quando a propriedade City for referenciada, e de forma que isto não precise estar explicitamente implementado na classe TCustomer
Wagner,
Com certeza… Pode esperar meu contato
Magno, o TMS Aurelius já foi lançado: http://www.tmssoftware.com/site/aurelius.asp
Já baixei e instalei o trial… Post sobre o Aurelius indo para o forno agora