Estratégias de Validação

Olá Guj’s!

Uma coisa que sempre me faz fritar os neurônios de tempos em tempos é a validação dos dados enviados pela aplicação cliente para a camada de serviço.

Por exemplo, hoje andei pensando em criar uma espécie de camada de validação utilizando um interceptor ou proxy do Spring. De acordo com minha imaginação, uma validação utilizando proxy seria mais ou menos assim:

AlunoServiceImpl (me preocupo apenas com a lógica de negócio)

public class AlunoServiceImpl implements AlunoService {
	
	private AlunoReposiory alunoReposiory;

	public Aluno criaAluno(Aluno aluno) {
		Aluno alunoCriado = alunoReposiory.cria(aluno);
		return alunoCriado;
	}
        
         // ....

}

AlunoValidationImpl (me preocupo apenas com a validação)

public class AlunoServiceImpl implements AlunoService {
	
	private AlunoReposiory alunoReposiory;

	public Aluno criaAluno(Aluno aluno) {
                AlunoValidate.validate(aluno); // é só um exemplo, supondo que eu esteja usando um framework de validação

                if (alunoReposiory.jaExiste(aluno)) {
                     throw new DuplicateEntityException();
                }
		
                return aluno;
	}
        
         // ....

}

Bom, é só uma idéia. Tem alguma solução melhor para lidar com as validações?

Thanks!

Eu não acho que essa seja uma boa idéia…

mas como vc tem lidado com as validações?

Acho que finalmente consegui chegar numa boa opção quanto a validação.

Como possuo uma interface que representa minha camada de serviço (AlunoService) e uma implementação defaul deste serviço (AlunoServiceImpl), basta apenas extender a implementação default do serviço e adionar no código características próprias de validação, utilizando sua api de validação preferida.

Exemplo:

public class MeuServicoComValidacao extends MeuServicoDefaultImpl {

   public void fazAlgumaCoisa(Coisa coisa) {
      // valida a coisa usando meu framework de validação preferido
      super.fazAlgumaCoisa(coisa);
   }

}

A vantagem é que se eu quiser mudar o mecanismo de validação, para reescrever uma outra camada de serviço com outro framework de validação. Bom, mas de qualquer jeito é tudo frescura.

O que não sei é se seria melhor extender uma implementação de serviço default ou implementar a interface de serviço e usar a implementação default por composição. De qualquer jeito fica a gosto do freguês.

Oi Thiago,

Na verdade, foi uma brincadeira interna a minha primeira resposta, por eu ser um defensor desse tipo de validação (usando proxies automáticos e separando regras de negócio de pré-condições).

Mas … realmente, como vc disse, e dessa vez é sério, eu não gosto do seu approach de extender a implementação e chamar super. Além do código não ter coesão, já que no fundo o método que você está chamando não faz o que estaria assinado no contrato dele, você ainda limita o reuso da camada de validação para outros motivos. Você poderia, por exemplo, usá-la para fazer a validação client side por ajax (como eu faço), e jogar a redundância de validação em javascript fora.

O que você acha?

[quote=mtoledo]Oi Thiago,

Na verdade, foi uma brincadeira interna a minha primeira resposta, por eu ser um defensor desse tipo de validação (usando proxies automáticos e separando regras de negócio de pré-condições).
[/quote]
Legal, bom saber que não sou o único que já pensou nesta idéia, rsrs.

Você tem razão.

eu ia implementar separando bem a validação de cada entidade, tornando possível reutilizar o código para outras validações. Eu ia tratar a validação como um outro módulo do projeto, responsável apenas por validação. Mas pensando bem no que você disse, este tipo de arquitetura deixa brechas para que o código não seja reutilizável.

Achei muito boas as suas colocações. No entanto, fiquei pensando sobre o uso de proxy e comecei achar que seria muito dificil implementar isso, por isso optei por criar um serviço mais especializado contendo as validações.

Um dos problemas que vejo utilizando o proxy para validação é que eu teria que criar um proxy com a validação, e depois criar um proxy do proxy para controlar a transação (no caso do spring, por exemplo).

Existem alternativas melhores? Já que você já está trabalhando com uma arquitetura como essas você poderia dar umas dicas ou um exemplo de código?

Thanks!
Thiago

[quote=Thiago Senna]Por eu ser um defensor desse tipo de validação (usando proxies automáticos e separando regras de negócio de pré-condições).
[/quote]

Tambem sou um defensor, implementei um estrutura que criar proxy dinamicos com filtros e um dos filtros que eu fiz foi um de autorização vou postar o codigo aqui derrepente possa ajudar!

Se vcs quiserem podem criticar tbem!

Interface Filtro

package br.com.comtex.vms.base.padroes.filtro;

import java.lang.reflect.InvocationHandler;

/**
 * Esta interface fixa uma interface comum para filtro 
 *
 * @since 1.0, 02/08/2006
 * @author Bruno Hansen
 */
public interface FiltroIF extends InvocationHandler {

}

Filtro de autorizacao

package br.com.comtex.vms.base.padroes.filtro.autorizacao;

import java.lang.reflect.Method;
import br.com.comtex.vms.base.autorizacao.AutorizadorIF;
import br.com.comtex.vms.base.autorizacao.SolicitadorIF;
import br.com.comtex.vms.base.padroes.filtro.FiltroIF;

/**
 * Esta classe representa um filtro de autorizacao 
 *
 * @since 1.0, 02/08/2006
 * @author Bruno Hansen
 */
public class FiltroAutorizacao implements FiltroIF {

	/**
	 * Objeto a ser filtrado 
	 *
	 * @since 1.0, 02/08/2006
	 * @author Bruno Hansen
	 */
	private final Object objetoFiltrado;
	
	/**
	 * Autorizador utilizado 
	 *
	 * @since 1.0, 02/08/2006
	 * @author Bruno Hansen
	 */
	private final AutorizadorIF autorizador;
	
	/**
	 * Solicitador utilizado
	 *
	 * @since 1.0, 02/08/2006
	 * @author Bruno Hansen
	 */
	private final SolicitadorIF solicitador;
	
	/**
	 * Constroi o filtro informando o objeto a ser filtrado, 
	 * o autorizador e o solicidador das requisicoes 
	 *
	 * @since 1.0, 02/08/2006
	 * @author Bruno Hansen
	 * @param _objetoFiltrado Objeto a ser filtrado
	 * @param _autorizador Autorizador a ser utilizado
	 * @param _solicitador Responsavel pelas solicitacoes feitas a partir deste filtro
	 */
	public FiltroAutorizacao(final Object _objetoFiltrado,final AutorizadorIF _autorizador,final SolicitadorIF _solicitador) {
		objetoFiltrado = _objetoFiltrado;
		autorizador = _autorizador;
		solicitador = _solicitador;
	}
	
	/**
	 * Aplica o filtro antes de executar o metodo solicitado 
	 *
	 * @since 1.0, 02/08/2006
	 * @author Bruno Hansen
	 * @param _objeto Objeto de onde partiu a solicitacao
	 * @param _metodo Metodo a ser executado
	 * @param _args Argumentos a serem passados para o metodo a ser executado
	 * @return Retorno do metodo a ser executado
	 * @throws Throwable Qualquer exececao do metodo invocado ou NaoAutorizado
	 */
	public Object invoke(final Object _objeto,final Method _metodo,final Object[] _args) throws Throwable {
		
		if(! autorizador.estaAutorizado(solicitador, _metodo.getName())){
			throw new NaoAutorizado();
		}
		
		return _metodo.invoke(objetoFiltrado, _args);
	}
}

Fabrica de Procurador filtrado

package br.com.comtex.vms.base.padroes.procurador;

import java.lang.reflect.Proxy;
import br.com.comtex.vms.base.padroes.filtro.FiltroIF;

/**
 * Esta representa uma fabrica de procurador que utiliza filtro
 *
 * @since 1.0, 02/08/2006
 * @author Bruno Hansen
 */
public class ProcuradorFiltrado {
	
	/**
	 * Cria o procurador com o filtro informado 
	 *
	 * @since 1.0, 02/08/2006
	 * @author Bruno Hansen
	 * @param _objetoReal Objeto ao qual deve ser criado o filtro (Este deve implementar ao menos uma interface onde todos os seus metodos possam gerar excecao, para manipular o retorno deste metodo)
	 * @param _filtro Filtro a ser adicionado no procurador
	 * @return Procurador
	 */
	public static Object criaProcurador(final Object _objetoReal, final FiltroIF _filtro){
				
		return Proxy.newProxyInstance(_objetoReal.getClass().getClassLoader(),
										_objetoReal.getClass().getInterfaces(),
										_filtro);
		
	}

}

Olá Bruno,

acho que a solução que você postou vai ajudar bastante. :smiley: A única diferença é que estou pensando em usar interceptors do spring. Algo que também achei legal no que você postou foi a possibilidade de usar o Proxy que vem com o próprio jdk. Essa pra mim é nova!

Bom, acho que é hora de começar a por a mão na massa para saber qual vai ser o gostinho de usar este tipo de validação. Estou prevendo alguns possíveis problemas, mas acho melhor parar de sofrer por antecipação, hehe!

Abraços
Thiago

E o uso do Hibernate Validator, vocês não acham que rola?
Além das anotações default, pode-se criar suas próprias anotações, específicas para o seu cenário.

O que vocês acham ?

[quote=carneiro]E o uso do Hibernate Validator, vocês não acham que rola?
Além das anotações default, pode-se criar suas próprias anotações, específicas para o seu cenário.

O que vocês acham ?[/quote]

Não havia me atentado ainda, mas gostei do Hibernate Validator. :smiley:

No entanto, o único problema que vejo para o meu caso é que o modelo ficaria acoplado a uma api específica de validação do hibernate, e meu objetivo era exatamente o contrário. Quero deixar o modelo livre de qualquer regra de validação ou outras API’s.

Agora, se fosse um projetinho usando hibernate do início ao fim, eu apostaria no Hibernate Validator com certeza. :wink:

O Hibernate Validator é muito bom! Ele faz vc colocar a validação onde ela sempre deve estar: nos seus POJOs. Além disso, no Jboss Seam (que usa o Hibernate) vc nem precisa validar os campos através dos validators do JSF. Basta vc colocar as regras nos POJOs. :wink:

Uau,

o JBoss Seam é realmente muito louco. Agora fiquei curioso e vou ver se aprende a brincar com ele. :mrgreen:

Legal que ele já propõe uma arquitetura, integrando JSF e EJB 3.0. Olhando por este ângulo, parece que EJB 3 + JSF podem se tornar uma solução interessante.

Caraca, esse JBoss Seam deu um nó na minha cabeça. Valeu pela dica, Taz! :wink: