Persistencia em um domain model
Categories: Design Patterns, Engenharia de software, Test-Driven development
-
Ultimamente estou usando uma abordagem diferente para a persistência. vinha adotando os pattern de ActiveRecord e Repository além do DataMapper popularmente conhecido como DAO. Aperfeiçoei com essa dica do CV no guj.
Apesar da persistência ser arquitetural e teoricamente não fazer parte do dominio dos seus processos de negócios, invariavelmente é a maior preocupação na construção de um software o “como” e “onde” um objeto tem que mudar seu estado persistido. Em outras palavras como isolar a persistência de seu domain model para que a transparência dessa operação diminua o código sem aumentar os pontos de manutenção com alta coesão e fraco acoplamento.
Estados do objeto
Os objetos do ponto de vista do modelo de um processo apresentam estados, como excluído, alterado e criado. Podemos adotar o conceito de Active Record no domain model e fazer com que seus objetos implementem uma interface que sem criatividade no momento chamarei de ActiveRecord:
public interface ActiveRecord {
public void carregado();
public void novo();
public void alterado();
public void excluido();
}
Podemos então criar uma classe abstrata seguindo o pattern Template Method para criar uma estrutura básica para todos nossos objetos que chamarei de AbstractActiveRecord:
public abstract class AbstractActiveRecord < t extends AbstractActiveRecord >
implements ActiveRecord {
public void carregado() {
Repository.carregar(this);
}
public void alterado() {
Repository.alterar(this);
}
…
}
Resolvi chamar o Repository dentro do Template do ActiveRecord para deixar isolado os DAOs da aplicação de criar apenas um ponto de manutenção em comum.
O conceito de Repository deixa o sistema isolado da infraestrutura de persistência funcionando como um espécie de banco de dados em memória que os objetos se dirigem ao perguntar por alguém (Alguém esse referente a uma entidade do dominio).
Observe que as classes do meu sistema devem extender a classe AbstractActiveRecord e implentar a interface ActiveRecord, mas isso não é se prender a um framework, porque essas classes fazem parte do dominio apesar de existirem para resolver um problema arquitetural, elas são do meu sistema, e estão independente de infraestrutura porque são relativos do dominio de qualquer forma.
Uma entidade do meu sistema fica assim:
public final class Cliente extends AbstractActiveRecord implements ActiveRecord {
private int id;
private String nome;
private String cpf;
//gets e sets
}
O Shoes tinha me dado um conselho de usar o Repository junto com o Active Record e DataMapper a um tempo atrás para isolar o modelo da persistência principalmente na lista de entidades, exemplo:
Cliente cliente = RepositorioClientes.carregaListaClientes();
Mas eu resolvi criar um repositório genérico para todas as entidades e dentro dele inserir uma chamada a uma fábrica de DAOs apropriado dependendo do tipo o objeto. Código do meu Repository:
public abstract class Repository {
public static void carregar(ActiveRecord record) {
System.out.println("carregou quem? " + record.getClass().getName());
//faz o trabalho de persistencia... pode fazer injeção etc...
//pode chamar uma Fabrica para criar o DAO a partir da classe
// FabricaDAO.criarDAO(record.getClass());
}
...
}
realizando um teste simples para ver como se comportaria:
public class TransacaoTest extends TestCase {
public void testTransacao(){
Cliente christiano = new Cliente();
christiano.setNome("christiano");
christiano.setCpf("999.999.999-99");
christiano.novo();
assertEquals(1, christiano.getId());
//acontece mais algumas coisas...
//e lá pelas tantas...
christiano.setNome("Christiano Martins");
christiano.alterado();
assertEquals("Christiano Martins", christiano.getNome());
}
}
O resultado foi como esperado, o processo vai modificando o estado dos objetos durante a transação e tornando o código mais legível, evitando chamar DAO dentro do meu modelo ou misturar código de framework como Hibernate ao meu domínio.
Com exemplos simples em transação praticamente CRUD a coisa funciona legal, quando precisamos alterar ou pra Lazy Loading ou Eager Loading dependendo do padrão da aplicação (deixo como padrão lazy), passamos um parâmetro para a estrutura como por exemplo:
normal
Cliente cliente = new Cliente(10); // esse construtor já invoca a estrutura e carrega as informações
com lazy Loading
Cliente cliente = new Cliente(10, ActiveRecord.LAZY_LOADING);
...
Construtor normal:
public Cliente(int id) {
super();
this.id = id;
this.carregado();
}
Construtor parametrizavel
public Cliente(int id, int LOADED_PARAMETER) {
super();
this.id = id;
this.carregado(LOADED_PARAMETER);
}
...
De qualquer forma podemos criar uma estrutura de modelo do domínio do meu processo isolada da arquitetura tecnológica facilitando a manutenção com padrões de projetos simples, código mais enxuto e transparência nas transações.



May 17th, 2006 at 8:37 pm
Repository + DAO + container de injecao de dependencia deixa o codigo bem coeso.
A abordagem de Active Record eh excelente, ja que visa um reaproveitamento grande de logica de persistencia. A questao eh se ela funciona quando se necessita de operacoes mais complexas e nao apenas crud de entidades.
Em linguagens dinamicas como Ruby e Python da pra se fazer maravilhas por causa do poder de meta-linguagem que elas possuem, mas em Java ficamos um pouco engessados. Eh complicado ter um Cliente.find_by_******* completamente dinamico como temos no Rails.
Como esta sendo a tua experiencia com isso?
[]s
May 17th, 2006 at 9:03 pm
MInha experiência com essa abordagem está até agora sendo produtiva, no caso do findBy como voce citou eu sempre coloco os metodos de busca no Repository, ficando algo como
Cliente cliente = new Cliente();
cliente.setNome(”christiano”);
Repository.carregar(cliente);
List listaClientes = Repository.carregarClientes();
etc… algo desse tipo, para as consultas.
para as ações de mudança de estado aí sim fica seguindo o ActiveRecord do tio Fowler!
cliente.alterado(); cliente.excluido()…
è muito dificil transportarmos a facilidade com certos conceitos como o Ruby por exmplo para Java por motivos obvios, mas estou planejando a adoção de DSL nos meus sistemas para dinamizar algumas coisas… tive experiencia muito boa no passado com PNUTS ( https://pnuts.dev.java.net/ ) e estou estudando o BeanShell… daqui um tmepo terei alguma experiencia para contar, claro que usando o bom senso, senão o negocio fica mostro e horrivel para manutenção, mas o Java vai ter que correr atrás do dotNet com essa questõ de suportar varias linguagens na JVM, graças a deus que existe concorrencia a altura, nada como isso para tornar a evolução constante…