Aqui vamos nós outra vez …
A) Imagine o seguinte codigo :
//1
Pessoas p = new Pessoa("Joana");
p.save();
p.delete();
p.save();
//2
Pessoas p = new Pessoa("Joana");
p.delete();
p.save();
Por aqui se vê que implicitamente temos que ter um estado para o objeto. Mesmo não usado ActiveRecord teriamos esse problema.
E ai vêm as perguntas:
Onde/como guardar o estado ?
Eu posso dar um delete antes de um save ?
E como eu controlo a transação ?
Existem padrões para o estado (State) e transação (Unit of Work)
e o delete não é problema. Afinal só se pode apagar o que já existia antes.
B) Para resolver o problema de não ter que escrever save() em todas as entidades existe outro padrão Layer Supertype. Que básicamente significa que se crie um classe abstracta que contém todas esses métodos comuns e delege para o repositorio usando this. O Layer Supertype é o que o Rails usa para capacitar as classes com os métodos de persistência.
C) Se vc prestar atenção na JPA será que ela coabita com os mesmos problemas. o repositorio equivale ao EntityManager.
Só que JPA não usa ActiveRecord.
Desta forma todas as operações têm que passar por outra classe , que é realmente aquela onde as injeções foram feitas à priori.
Se vc quer poder usar o new nos seus objetos de dominio a coisa complica-se porque não existe um ponto de extensão (a menos que use AOP, claro) Sem usar AOP vc tem duas alternativas:a) cria os objetos usando um metodo fábrica (Pessoa p = Pessoa.pessoa(“Joana”)
ou usar processamento de bytecode (que é algo abaixo de AOP).
Eu ja pensei nisto durante muito tempo e a solução mais eficiente , quanto a mim , é usar o processamento de bytecode, já que não obriga a criar metodos static nas classes de entidade.
funciona assim
Vc pode criar o objeto com new e ele será um objeto comum.
Como ele foi criado com new e não veio do repositorio, ele tem o estado “novo” por padrão. Só que não ha nenhuma variável a dizer isso, é uma convenção.
Ai vc faz os get/set que tem a fazer e usa os metodos que tiver que usar , etc… no fim, vc simplesmente faz Repository.save§;
Repare que se estiver num Unit Of Work esse save só será feito no fim , quando houver um commit e não será feito por si e sim pelo objeto controlador. Então quando eu digo “vc faz” eu quero dizer “alguem faz”, o programador, ou a infraestrutura.
Ao fazer o save, dentro do repositorio o objeto Pessoa é processado via manipulação de bytecode e ele vira um outro objeto , digamos, PessoaProxy. A sacada é que PessoaProxy é construida dinamicamente como uma subclasse de pessoa. Isso permite que a partir dai não mais estejamos preocupados com a classe Pessoa em si, já que PessoaProxy tem a mesma interface. Além disso PessoaProxy tem o estado de edição como uma variável. Para melhorar , podemos ainda fazer PessoaProxy implementar outras interfaces genericas que serão as que o DAO vê e manipula (sei lá, por exemplo , Persistable).
Vc pode ainda usar a leitura de anotações em Pessoa para criar PessoaProxy, por exemplo, saber quais campos são a chave.
Este processo é muito poderoso. E algo assim que a JPA usa.
Quando vc der um find no repository, ele vai retornar instancia de PessoaProxy , que são Pessoa tb. Então o codigo a seguir funciona
Pessoa p = repository.findByKey(12);
Vc pode fazer instanceof , que o resultado vai ser true. p, é uma Pessoa, embora a classe seja realmente PessoaProxy que herda de Pessoa dinamicamente.
Agora, vc tem um vantagem extra. Vc pode interceptar todos os set que forem dados em p e saber se o estado de persistencia mudou. Esse estado será usado quando um outro save§ for dado.
Moral da historia, Repositorio trabalha com proxies e as classes de entidade não tem que fazer nada especial (além de algumas anotações tlv)
Agora, como eu não procuro usar o ActiveRecord eu tb não sei como usar este esquema com o ActiveRecord. Eu até já tentei usar o ActiveRecord mas - como diz o Fowler - ele realmente só funciona para coisas muito simples (por isso funciona no Rails). Ele não funciona num nivel enterprise como o JPA ou o hibernate
Espero que isto te ajude…