Opnião sobre arquitetura e repositorios

Pessoal estou iniciando um projeto e gostaria de saber de vocês o que acham de alguns detalhes que estou implementando e o que preciso mudar também, criei um repositorio generico com a seguinte interface:

este repositório cobre bem os filtros do sistema e pode ser usado com o conceito de fluentInterfaces:

public interface Repositorio<ENTIDADE,REPOSITORIO> {

	Integer contagem();
	List<ENTIDADE> listagem();
	List<ENTIDADE>  listagem(int inicio,int numRegistros);
	REPOSITORIO ordenadoPor(String campo,boolean descending);
	REPOSITORIO igual(String campo,Object valor);
	REPOSITORIO naFaixa(String campo,Object valorInicial,Object valorFinal);
	REPOSITORIO contendo(String campo,String valor);
	ENTIDADE resultadoUnico();
	void salvar(ENTIDADE entidade);
	void remover(ENTIDADE entidade);
	
}

um exemplo de utilização seria este:

repositorio.igual(“nome”,“carlos alberto”).ordenadoPor(“id”,true).listagem();

isto me retorna uma lista de todos com o nome carlos alberto ordenado por id:

segue a implementação da classe:


@SuppressWarnings("unchecked")
public class RepositorioImpl<ENTIDADE,REPOSITORIO> implements Repositorio<ENTIDADE,REPOSITORIO> {

	protected Class<ENTIDADE> persistentClass;

	public RepositorioImpl() {
		Object entidade =  this.getClass();
		while(! (entidade instanceof ParameterizedTypeImpl)){
			entidade = ((Class<ENTIDADE>) entidade).getGenericSuperclass();
		}
		this.persistentClass = (Class<ENTIDADE>) ((ParameterizedTypeImpl) entidade).getActualTypeArguments()[0];
	}

	@In
	protected Session session;

	@In
	protected EntityManager em;

	protected Criteria criteria = null;


	public void setSession(Session session) {
		this.session = session;
	}

	public void setEm(EntityManager em) {
		this.em = em;
	}

	protected void initCriteria() {
		if (criteria == null) {
			criteria = session.createCriteria(persistentClass);
		}
	}
	
	public Integer contagem() {
		initCriteria();
		criteria.setProjection(Projections.rowCount());
		Integer result = (Integer) criteria.uniqueResult();
		criteria = null;
		return result;
	}

	public REPOSITORIO contendo(String campo, String valor) {
		initCriteria();
		criteria.add(Restrictions.ilike(campo, valor, MatchMode.ANYWHERE));
		return (REPOSITORIO) this;
	}

	public REPOSITORIO igual(String campo, Object valor) {
		initCriteria();
		criteria.add(Restrictions.eq(campo, valor));
		return (REPOSITORIO) this;
	}

	public ENTIDADE resultadoUnico() {
		initCriteria();
		ENTIDADE result = (ENTIDADE) criteria.uniqueResult();
		criteria = null;
		return result;
	}

	public List<ENTIDADE> listagem() {
		initCriteria();
		List<ENTIDADE> result = criteria.list();
		criteria = null;
		return result;
	}

	public List<ENTIDADE> listagem(int inicio, int numRegistros) {
		initCriteria();
		criteria.setFirstResult(inicio);
		criteria.setMaxResults(numRegistros);
		List<ENTIDADE> result = criteria.list();
		criteria = null;
		return result;
	}

	public REPOSITORIO naFaixa(String campo, Object valorInicial, Object valorFinal) {
		initCriteria();
		criteria.add(Restrictions.between(campo, valorInicial, valorFinal));
		return (REPOSITORIO) this;
	}

	public REPOSITORIO ordenadoPor(String campo, boolean descending) {
		initCriteria();
		criteria.addOrder(descending ? Order.desc(campo) : Order.asc(campo));
		return (REPOSITORIO) this;
	}

	public void remover(ENTIDADE entidade) {
		ENTIDADE attachedEntity = em.merge(entidade);
		em.remove(attachedEntity);
	}

	public void salvar(ENTIDADE entidade) {
		Entidade entity = (Entidade) entidade;
		if (entity.getId() != null)
			em.merge(entity);
		else
			em.persist(entity);

	}

}

agora para implementações uso o seguinte crio uma interface para o repositório que gostaria de criar e extendo a classe que implementa o repositorio, assim:

public interface RepositorioUsuario extends Repositorio<Usuario, RepositorioUsuario> {

}
public class DAOUsuario extends RepositorioImpl<Usuario, RepositorioUsuario> implements RepositorioUsuario {

}

caso tenha algum método especial de consulta que não é coberto pelo repositorio genérico, posso implementar no repositorio específico do sistema.

Esta é uma boa abordagem?

Não.

Vou falar algo que não é só pra você, mas pra todos aqui do GUJ: Não usem DAO ou Repositório genérico. Motivos:

  1. Não representa uma boa abstração, porque todos os métodos representam uma ou duas linhas de código de JPA/Hibernate.

  2. Por ser genérico, acaba vazando alguma abstração, e forçando a camada de negócio a decidir como deverá ser feita a consulta ao banco.

  3. É baseada em herança, e herança cria dependencias em tempo de compilação.

  4. Criar uma classe em que todos tem dependência é péssimo. Implica que sempre que precisar modificar esta classe, todo o resto precisa ser testado. E não é raro as pessoas simplesmente desistir de alterar a classe genérica.

Então, por favor, a você e aos zilhões de programadores que mandam seus DAOs genéricos para o GUJ, parem de fazer isso. Criem os DAOs, mas deixem que eles mesmos instanciem e manipulem a Session ou a EntityManager.

http://www.guj.com.br/posts/list/60916.java

Não sei se já lestes, caso sim, ignore minha postagem.

[quote=giulianocosta]http://www.guj.com.br/posts/list/60916.java

Não sei se já lestes, caso sim, ignore minha postagem.[/quote]

Sim já havia visto e estava trabalhando esta implementação com base na especificação dos autores do post.

Obrigado, mesmo assim.

Isso que vc desenvolveu não é um Repositorio é um Builder de QueryObject

A ideia é boa ,mas vc quereria algo assim :

[code]Query q = QueryBuilder.procura(Pessoa.class).igual(“nome”,“carlos alberto”).ordenadoPor(“id”,true);

List resultado = repositorio.find(query);[/code]

A query é independente do repositorio e da classe procurada.

Leonardo3001 explico o motivo do porque usar generics e herança em alguns casos.

acho que você deve ter se baseado apenas no título do post e nem olhado as implementações, o método save contém mas código do que simplesmente duas linhas de JPA/Hibernate, e ela centraliza as operações de salvamento, não repetindo códigos em diversas implementações apenas para saber se quero fazer um merge ou um persist no banco, com respeito ao método remove, realmente é apenas uma linha de código mas mesmo assim você ainda teria código repetido em suas classes de implementação.

Caso notou tenho uma interface que é baseada na interface genérica, nela posso colocar detalhes mais específicos que não são atendidos pela interface genérica, sempre ao usar não uso a implementação da interface genérica, mas sim a implementação da interface de négocios, ou o RepositorioUsuario, a interface genérica foi construida de forma a fazer uso do fluent interfaces, permitindo assim uma abstração sobre o mecanismo de persistência.

Mas foi para isto que eu criei as interfaces e as classes que estendem da genérica, para que as implementações da classe genérica possam ser bastante estáveis, e você não usa uma suite de testes no sistema? qualquer alteração você realmente saberá assim que os testes forem executados e utilizando o maven fica mais fácil visto que a execução pode ser automatizada ainda no build.

Caso não tenha lido o post referenciado pelo usuário giulianocosta, a uma citação do Shoes que a a implementação de um repositorio pode ser um dao visto que o mesmo representa o acesso aos dados a nível de banco de dados, mas poderia ser qualquer classe até um sistema de persistencia em XML ou algo semelheante, mas geralmente as interfaces de um repositorio são implementadas para acesso em um dao.

A proposito caso não tenha entendido gostaria de sugestões, então porque não posta a sua abordagem para que eu possa analisar e ver então como alguém como você faz.

Obrigado.

Leonardo3001

[quote=euprogramador]Leonardo3001 explico o motivo do porque usar generics e herança em alguns casos.

acho que você deve ter se baseado apenas no título do post e nem olhado as implementações, o método save contém mas código do que simplesmente duas linhas de JPA/Hibernate, e ela centraliza as operações de salvamento, não repetindo códigos em diversas implementações apenas para saber se quero fazer um merge ou um persist no banco, com respeito ao método remove, realmente é apenas uma linha de código mas mesmo assim você ainda teria código repetido em suas classes de implementação.[/quote]

Beleza, o método save() tem cinco linhas. Ainda assim, não invalida o ponto central: não há ganho de abstração. Repetir código é algo que involve bom senso, por exemplo: objetos Date são criados em várias partes do sistema, mas não significa que devemos abstrair essa criação. O problema é que você só muda o problema de lugar, antes era vários lugares que dependiam de Date, agora são vários lugares de dependem da abstração de Date. O raciocínio com o seu save() é o mesmo.

[quote=euprogramador][quote=Leonardo3001]
2) Por ser genérico, acaba vazando alguma abstração, e forçando a camada de negócio a decidir como deverá ser feita a consulta ao banco.
[/quote]

Caso notou tenho uma interface que é baseada na interface genérica, nela posso colocar detalhes mais específicos que não são atendidos pela interface genérica, sempre ao usar não uso a implementação da interface genérica, mas sim a implementação da interface de négocios, ou o RepositorioUsuario, a interface genérica foi construida de forma a fazer uso do fluent interfaces, permitindo assim uma abstração sobre o mecanismo de persistência.[/quote]

O vazamento de abstração é semântico. Um repositório não deveria ter métodos do tipo save() ou listagem(), é preferível nomes mais a ver com o negócio. Usar um repositório genérico impede que se crie os métodos com nomes de negócio.

[quote=euprogramador][quote=Leonardo3001]
4) Criar uma classe em que todos tem dependência é péssimo. Implica que sempre que precisar modificar esta classe, todo o resto precisa ser testado. E não é raro as pessoas simplesmente desistir de alterar a classe genérica.
[/quote]

Mas foi para isto que eu criei as interfaces e as classes que estendem da genérica, para que as implementações da classe genérica possam ser bastante estáveis, e você não usa uma suite de testes no sistema? qualquer alteração você realmente saberá assim que os testes forem executados e utilizando o maven fica mais fácil visto que a execução pode ser automatizada ainda no build.[/quote]

  1. Ainda assim, todos dependem da interface. Ainda assim, a customização depende de herança.
  2. O uso de testes unitários não te livra da obrigação de usar outras boas práticas, como gerenciar as dependências. Evite criar qualquer classe em que todos dependem. mesmo com testes automatizados.

[quote=euprogramador][quote=Leonardo3001]
Então, por favor, a você e aos zilhões de programadores que mandam seus DAOs genéricos para o GUJ, parem de fazer isso. Criem os DAOs, mas deixem que eles mesmos instanciem e manipulem a Session ou a EntityManager.
[/quote]

Caso não tenha lido o post referenciado pelo usuário giulianocosta, a uma citação do Shoes que a a implementação de um repositorio pode ser um dao visto que o mesmo representa o acesso aos dados a nível de banco de dados, mas poderia ser qualquer classe até um sistema de persistencia em XML ou algo semelheante, mas geralmente as interfaces de um repositorio são implementadas para acesso em um dao.[/quote]

Não tenho nada contra DAOs, e sei muito bem que a origem dos dados persistentes pode vir de qualquer outra coisa. Mas não faria uso de DAO genérico, só isso.

Leonardo3001, então é para melhorar a dependencia entre as camadas e usar o exemplo do queryObject para consultas dentro dos repositorios?

Dá uma olhada neste tópico que trata um pouco dessa parte.
http://www.guj.com.br/posts/list/139841.java