Validação de atributos - Melhores práticas!

30 respostas
C

Galera.

Vamos supor que temos a seguinte aplicação em JavaEE.

public class Livro{

	@NotEmpty
	private String nome;
	@NotEmpty
	private double valor;

}
public class LivroDAO{

	public void salvar(Livro livro){
		executa o insert no banco
	}

}
public class Cadastro extends HttpServlet{

	protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		Livro livro = new Livro(req.getParameter("nome"),req.getParameter("valor"));
		ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
		Validator validator = factory.getValidator();
		Set<ConstraintViolation<Livro>> violations = validator.validate(livro);
		if(violations.size() <= 0){
			new LivroDAO.salvar(livro);
		}
	}

}

Até ai legal… é o padrão de qualquer aplicação.
Porém, tenho a seguinte dúvida:

A validação é feita no servlet, e por este motivo, um outro programador pode realizar o seguinte código:

new UserDAO.save(new Livro("",90));

Supondo que o banco de dados não valide nada (apenas seja responsabilidade da aplicação java), essa abordagem não me garante a integridade da aplicação, no caso o @NotEmpty private String nome.

Existe alguma forma de colocar a validação no processo final de criação do usuario, como no UserDAo.salva, por exemplo???

30 Respostas

C

Existe, no metodo salvar faça assim

if(user.getNome() == null || user.getNome().isEmpty()){
//dispara exececao
}

mas acho que existe alguma anotacao para tamanho minimo, ai voce pode coloca o tamanho minimo de 1 por exemplo forçando a propriedade a ter valor…

R

Por favor, sempre que for postar código, o poste entre as tags [code][ /code].

Leia nosso 'How To' antes de postar: http://www.guj.com.br/java/287476-gujnautas-how-to

C

Ok. Obrigado pela dica! Já corrigi o tópico.

Rodrigo Sasaki, obrigado pela resposta!

Na verdade corrigi o exemplo. O certo é o @NotEmpty.
Porém a minha duvida é o local que a validação é feita.

No exemplo acima (utilizado pela maioria dos sistemas, segundo minha experiencia profissinal), a validação foi feito no servlet e isso realmente funciona. Mas se um novo programador instancia a classe UserDAO e passa qualquer tipo de usuario, a classe irá realizar a operação no banco. Apesar das regras de validações estarem no model (com as anotações), a execução da validação ocorre apenas no Servlet, o que permite que um novo desenvolvedor faça o seguinte:

new UserDAO.save(new Livro("",90));

Como centralizar o processo de criação de usuário, de forma que ninguém consiga enviar uma ordem de criação sem que ela seja validada??

C

CodeDeveloper:
Galera.

Vamos supor que temos a seguinte aplicação em JavaEE.

public class Livro{

	@NotEmpty
	private String nome;
	@NotEmpty
	private double valor;

}
public class LivroDAO{

	public void salvar(Livro livro){
		executa o insert no banco
	}

}
public class Cadastro extends HttpServlet{

	protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		Livro livro = new Livro(req.getParameter("nome"),req.getParameter("valor"));
		ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
		Validator validator = factory.getValidator();
		Set<ConstraintViolation<Livro>> violations = validator.validate(livro);
		if(violations.size() <= 0){
			new LivroDAO.salvar(livro);
		}
	}

}

Até ai legal… é o padrão de qualquer aplicação.
Porém, tenho a seguinte dúvida:

A validação é feita no servlet, e por este motivo, um outro programador pode realizar o seguinte código:

new UserDAO.save(new Livro("",90));

Supondo que o banco de dados não valide nada (apenas seja responsabilidade da aplicação java), essa abordagem não me garante a integridade da aplicação, no caso o @NotNull private String nome.

Existe alguma forma de colocar a validação no processo final de criação do usuario, como no UserDAo.salva, por exemplo???

C

Acredito que o voce quer nao tem como ser feito, quanto entrar alguem novo na sua equipe voce deve lhe mostrar o jeito certo de se fazer as coisas e acompanhar o caboblo no começo, e antes dele fazer comits no projeto refiçar junto com ele o que ele fez e se tiver algo errado mostrar explicar porque esta errado e mostrar o jeito certo, mas encapsulara para fazer so pela servlet so se voce fazer deuso da orientação a objetos e jogar a logica da persistencia dentro da servlet…

C

Mas Cristian,

A orientação a objetos tem o objetivo de sempre alcançar o reuso. O correto não seria deixar a lógica de validação junto com a de inserção no Banco de Dados??
Digo como uma espécia de método:

public class UserFacade{
	public Validator criar(Livro livro){

		ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
		Validator validator = factory.getValidator();
		Set<ConstraintViolation<Livro>> violations = validator.validate(livro);
		if(violations.size() <= 0){
			new LivroDAO.salvar(livro);
		}
		return validator;
	}
}

Desta forma, um novo devenvolvedor chamaria a UserFacade.criar e garantiria a consistencia dos dados em qualquer parte do sistema que essa função fosse chamada. E caso a validação não fosse satisfeita, o método ainda retornaria um Validator com os erros de validação.
A abordagem está correta?

C

com uma pergunta eu quebro essa sua teoria e ai vai ela:

o que voce fara para o programador naum chamar diretamente o LivroDao e sim a classe UserFacade ?

se voce garantir que ninguem conseguira instanciar o LivroDAO e chama-lo diretamente isso funcionara perfeitamente, naum fecho outra falha, mas se a classe LivroDAO poder ser instanciada de outros lugares do sistema e naum apenas de dentro do Facade seu esquema esta furado…

S

quote=CodeDeveloper

public class Cadastro extends HttpServlet{

	protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		Livro livro = new Livro(req.getParameter("nome"),req.getParameter("valor"));
		ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
		Validator validator = factory.getValidator();
		Set<ConstraintViolation<Livro>> violations = validator.validate(livro);
		if(violations.size() <= 0){
			new LivroDAO.salvar(livro);
		}
	}

}

Até ai legal… é o padrão de qualquer aplicação.
[/quote]

Sobre como fazer sua propria API de validação. É um pouco mais profundo do que parece.


Porém, tenho a seguinte dúvida:

A validação é feita no servlet, e por este motivo, um outro programador pode realizar o seguinte código:

new UserDAO.save(new Livro("",90));

Supondo que o banco de dados não valide nada (apenas seja responsabilidade da aplicação java), essa abordagem não me garante a integridade da aplicação, no caso o @NotEmpty private String nome.

Existe alguma forma de colocar a validação no processo final de criação do usuario, como no UserDAo.salva, por exemplo???

A validação pode ser colocada onde vc quiser. Aliás, em tese, sempre que vc muda de camada, vc deveria fazer a validação. O que acontece é que existem validações de diferentes tipos.
Na camada de apresentação ( o servlet) vc valida se o input é válido. Ou seja, se o usuário não está tentando driblar alguma regra (por exemplo colocar uma data que não existe), se preencheu tudo o que deveria, etc…
É é a validação básica.

Depois vc valida no serviço. Aqui é uma validação mais de negocio. Se a informação não é repetida, se é permitido, se faz sentido de negocio, etc… estas são validações mais especificas e mais próximas ao negócio.

Depois, se vc quiser vc pode validar na camada de persistência também. É um bocado trivial pq vc só vai validar se não está vazio etc… mas em tese as outras camadas já fizeram isso. É apenas uma segurança a mais.
É bom vc por validação em todas as camadas, exactamente para escapar desse cenário que colocou que “o programador pode fazer asssim e estragar tudo”, mas depende da robustez que o sistema precisa nem sempre é necessário. A regra geral é : quanto mais publico, mais precisa de validação.

Utilizar uma api como a do hibernate validation ajudam vc apenas na camada de persistência. Nas outras as regras são normalmente especificas e dependentes do negocio. É claro que vc pode usar api de validação (Beans Validation, por exemplo), ha muitas por ai, ou vc pode fazer a sua como indiquei - não é difícil. O ponto é que vc use uma api de validação. Compartimentalize e isole bem as regras , use composição de validadores para não reimplementar a mesma regras duas vezes e se possível use annotations. Mas seja consciente que annotations não funciona em todos os casos, então tenha uma forma mais programática sempre à mão.

Validação é muito importante e não é tratada com o devido respeito. Validação é uma parte do domínio e muito importante no modelo de desenvolvimento orientado ao domínio.

C

Agradeço a interação de todos!

Cristian,

Um método para não deixar o desenvolvedor chamar diretamente o “UserDAO.salva”, seria deixar a classe abstrata, e então fazera herança com o UserFacade e utilizar o método apenas dentro da Facade. Porém, vejo que isso não faz muito sentido, pois desta forma iria dizer que UserFacade é filha de UserDAO, o que não é o correto, porém, resolveria o prolema de segurança. Agora se você me pergunta: E se o desenvolvedor fizesse outra classe qe herdasse o UserDAO e chamasse o método criar de dentro dela. Nesta caso não teria resposta…rsrs

sergio,

Concordo plenamento com o que você disse: “Validação é muito importante e não é tratada com o devido respeito”. E é exatamente po isso que estou querendo saber mais sobre melhores pratocas, pois nas empresas que trabalhei já encontrei muitos furos como este que exemplifiquei.

O que você sujere é realizar a validação em todas as camadas necessárias, mesmo que pra isso eu tenha que duplicar código de válidação? Esta abordagem não causaria um grande problema no caso da regra mudar, como por exemplo, ao invés do campo não poder ser nulo, ele passar a aceitar apenas caracteres com um número minimo de 4. Desta forma eu teria que alterar todas as partes da validação que antes validavam o not null, para validar agora o mínimo de 4 caracteres?

S

CodeDeveloper:

sergio,

Concordo plenamento com o que você disse: “Validação é muito importante e não é tratada com o devido respeito”. E é exatamente po isso que estou querendo saber mais sobre melhores pratocas, pois nas empresas que trabalhei já encontrei muitos furos como este que exemplifiquei.

O que você sujere é realizar a validação em todas as camadas necessárias, mesmo que pra isso eu tenha que duplicar código de válidação? Esta abordagem não causaria um grande problema no caso da regra mudar, como por exemplo, ao invés do campo não poder ser nulo, ele passar a aceitar apenas caracteres com um número minimo de 4. Desta forma eu teria que alterar todas as partes da validação que antes validavam o not null, para validar agora o mínimo de 4 caracteres?

Acho que vc passou batido quando eu falei de usar composição. Não vc não replica o codigo. Vc cria um validador e o usa várias vezes. Se o codigo mudar, vai mudar só dentro do validador.
A composição de validadores (veja no link que eu mostro como se faz) é a sua arma para não repetir definição de regras, mas repetir o uso da regra.

C

sergio,

Realmente você citou a composição, desculpe-me. Analisei o link passado e acho entendi o que você quis dizer.
Veja se a abordagem está correta, por favor:

De modo geral, a idéia é ter uma classe que valide Livro, centralizando as regras de validações e utilizando um validador.

public class LivroValidador{

	public boolean isValid(Livro livro){
		// Utilizar aqui o Validar e centralizar as regras de validação
	}



}

Então, utilizar essa classe em todos os pontos necessários para garantir a persistencia, como por exemplo em LivroDAO, CadastroController e etc.

public class CadastroController extends HttpServlet{  
      
	protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {  
      
		Livro livro = new Livro(req.getParameter("nome"),req.getParameter("valor"));
		// Utilização da classe validadora  
		if(new LivroValidador.isValid(livro)){  
			new LivroDAO.salvar(livro);  
 		}  
        }  
      
}
public class LivroDAO{  
      
	public void salvar(Livro livro){ 
		// Utilização da classe validadora 
		if(new LivroValidador.isValid(livro)){ 
			//executa o insert no banco 
		} 
	}  
      
}

1º: Entendi corretamente, sergio?
2º: Neste caso o método isValid() está retornando um boolean. Porém ele também poderia retorna um objeto Validador para ser tratado por quem chamou a função, correto?

T

Você pode user um dos callbacks que o JPA tem: PrePersist, PreUpdate, PreDelete…

http://docs.jboss.org/hibernate/orm/4.0/hem/en-US/html/listeners.html

No callback vc pode disparar uma exceção caso o object não esteja válido.

C

tveronezi,

Não estou utilizando JPA.
Sabe me dizer se a abordagem que mencionei está correta ou na classe DAO não deve conter validação?

S

CodeDeveloper:
sergio,

Realmente você citou a composição, desculpe-me. Analisei o link passado e acho entendi o que você quis dizer.
Veja se a abordagem está correta, por favor:

De modo geral, a idéia é ter uma classe que valide Livro, centralizando as regras de validações e utilizando um validador.

public class LivroValidador{

	public boolean isValid(Livro livro){
		// Utilizar aqui o Validar e centralizar as regras de validação
	}



}

Então, utilizar essa classe em todos os pontos necessários para garantir a persistencia, como por exemplo em LivroDAO, CadastroController e etc.

public class CadastroController extends HttpServlet{  
      
	protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {  
      
		Livro livro = new Livro(req.getParameter("nome"),req.getParameter("valor"));
		// Utilização da classe validadora  
		if(new LivroValidador.isValid(livro)){  
			new LivroDAO.salvar(livro);  
 		}  
        }  
      
}
public class LivroDAO{  
      
	public void salvar(Livro livro){ 
		// Utilização da classe validadora 
		if(new LivroValidador.isValid(livro)){ 
			//executa o insert no banco 
		} 
	}  
      
}

1º: Entendi corretamente, sergio?

Sim. O único ponto que faltou é que não é o mesmo validador que vc usa em todo o lugar porque as regras que vc está validando em cada camada são diferentes. O DAO por exemplo vai validar se o campo está preenchido, mas ele não sabe validar se o valor preenchido é correto conforme a regra de negocio. Isso é o que o service vai fazer. Em geral eu mantenho validadores de persistencia, que só uso nos daos e validadores de dominio que uso nos services. As regras dos validadores de dominio são realmente regras, as dos do DAO são salvaguardas (safety-check)

Não. Um validador que retorna um validador ? Ou seja, um objeto que se retorna a si mesmo ?

Um opção mais robusta é usar um método validate que não retorna um boolean. Retorna um objeto de resultado da validação (ValidationResult) e esse objeto que tem um isValid(). Lembre-se que quando o objeto não é válido, a razão de invalidação é mais importante que o boolean, normalmente vc vai querer ter acesso a essas razões. Essas razões ficam no ValidationResult numa simples lista ou coisa assim ( no link que eu passei do javabuilding tem a implementação de um ValidationResult relativamente sofisticado que vc pode usar como modelo). O ponto é, isValid é pouca informação. Codigo verdadeiros têm um else que mostra ao usuario o problema.

public class CadastroController extends HttpServlet{    
            
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {    
            
            Livro livro = new Livro(req.getParameter("nome"),req.getParameter("valor"));  
            // Utilização da classe validadora    

             LivroValidator validator = new LivroValidator();

             ValidationResult result = validator.validate(livro);

            if(result.isValid()){    
                new LivroDAO.salvar(livro);    
            }    else {
                // mostra os erros para o usuario
                resp.setAtribute(result.getInvalidationReasons().get(0).getMessage()); // pega a mensagem da primeira razão de invalidação
            }
         }    
            
    }

Só a camada de ui que pode diretamente mostrar as mensagens.
Na camada de serviços e de dao o else gera uma Exception ( normalmente uma ValidationException que contém o objeto ValidationResult).

O ponto é que são as mensagens que são importantes e não se é válido ou não. Ou seja, é mais importante saber porque é inválido, do que saber que é válido.

C

sergio,

Muito obrigado pela ajuda e pela ótima explicação detalhada!
Entendi realmente o que você exemplificou e agora vou aplicar isto no código e analisar o andamento.

Acredito que isso resolva o meu problema!
Qualquer coisa volto a postar neste tópico, ok??

Grande abraço!!

J

Você precisa decidir se quer criar algo no prazo ou um sistema à prova de tolos.

Se um programador não sabe o que está fazendo mas mesmo assim pode usar o sistema pra realizar alguma operação no banco de dados que o deixaria em estado inconsistente, então você tem problemas muito mais sérios do que como fazer validação em servlets.

Sinceramente acho que você está pensando muito sobre algo que deveria ser simples. Normal, acontece muito com quem está iniciando.

C

JoseIgnacio,

Correto. Mas você concorda que não necessariamente um programador não sabe o que faz, mas por falta de informação em um projeto extremamente corrido, ou por uma distração (algo que pode acontecer com qualquer pessoa exausta e sob pressão), este problema poderia ocorrer?

Você aconselha então não se preocupar com a validação na classe DAO e tentar ao máximo ter programadores totalmente alinhados com o projeto e ótimos técnicamente? Desta forma, supostamente ninguém faria diretamente um “new LivroDAO().salvar(new Livro());” ?

J

CodeDeveloper:
JoseIgnacio,

Correto. Mas você concorda que não necessariamente um programador não sabe o que faz, mas por falta de informação em um projeto extremamente corrido, ou por uma distração (algo que pode acontecer com qualquer pessoa exausta e sob pressão), este problema poderia ocorrer?

Você aconselha então não se preocupar com a validação na classe DAO e tentar ao máximo ter programadores totalmente alinhados com o projeto e ótimos técnicamente? Desta forma, supostamente ninguém faria diretamente um “new LivroDAO().salvar(new Livro());” ?

Exato. Se alguém pode fazer isso… compilar, testar, fazer o deploy, e ninguém percebeu nada, é porque existem problemas mais sérios que estão fora do escopo e alcance do seu software. Validação tb não vai impedir que alguém introduza incosistências conectando diretamente com o banco de dados. Então pra que se preocupar?

C

JoseIgnacio:
CodeDeveloper:
JoseIgnacio,

Correto. Mas você concorda que não necessariamente um programador não sabe o que faz, mas por falta de informação em um projeto extremamente corrido, ou por uma distração (algo que pode acontecer com qualquer pessoa exausta e sob pressão), este problema poderia ocorrer?

Você aconselha então não se preocupar com a validação na classe DAO e tentar ao máximo ter programadores totalmente alinhados com o projeto e ótimos técnicamente? Desta forma, supostamente ninguém faria diretamente um “new LivroDAO().salvar(new Livro());” ?

Exato. Se alguém pode fazer isso… compilar, testar, fazer o deploy, e ninguém percebeu nada, é porque existem problemas mais sérios que estão fora do escopo e alcance do seu software. Validação tb não vai impedir que alguém introduza incosistências conectando diretamente com o banco de dados. Então pra que se preocupar?

É um ponto de vista interessante. Na verdade como você mesmo disse, os iniciantes devem ter essa preocupação quando estão começando… eu tenho a impressão que tenho que fazer de tudo ao meu alcançe para garantir a consistencia dos dados. Porém concordo com você também. Até que ponto isso vale a pena?

Obrigado por compartilhar Jose!!

J

CodeDeveloper:

É um ponto de vista interessante. Na verdade como você mesmo disse, os iniciantes devem ter essa preocupação quando estão começando… eu tenho a impressão que tenho que fazer de tudo ao meu alcançe para garantir a consistencia dos dados. Porém concordo com você também. Até que ponto isso vale a pena?

Obrigado por compartilhar Jose!!

Programadores experientes também se preocupam com consistência dos dados. A diferença é que eles são mais eficientes, porque ao invés de validar toda vez que o sistema toca no banco de dados, o programador OO garante consistência usando o próprio modelo de objetos.

Neste caso, o banco de dados é 100% irrelevante e pode ser mapeado pra qualquer schema que suportar o seu modelo de objetos, seja relacional, NoSQL ou arquivos.

S

JoseIgnacio:
CodeDeveloper:
JoseIgnacio,

Correto. Mas você concorda que não necessariamente um programador não sabe o que faz, mas por falta de informação em um projeto extremamente corrido, ou por uma distração (algo que pode acontecer com qualquer pessoa exausta e sob pressão), este problema poderia ocorrer?

Você aconselha então não se preocupar com a validação na classe DAO e tentar ao máximo ter programadores totalmente alinhados com o projeto e ótimos técnicamente? Desta forma, supostamente ninguém faria diretamente um “new LivroDAO().salvar(new Livro());” ?

Exato. Se alguém pode fazer isso… compilar, testar, fazer o deploy, e ninguém percebeu nada, é porque existem problemas mais sérios que estão fora do escopo e alcance do seu software. Validação tb não vai impedir que alguém introduza incosistências conectando diretamente com o banco de dados. Então pra que se preocupar?

A única certeza que existe num projeto de software é aquilo que o codigo faz. Se o codigo dá uma exeção alertando um problema ele tem razão. O problema é visto por alguem (normalmente o usuário, porque ninguem testa a coisa direito) e leva a um bug que é corrigido. Se a verificação não está lá, simplesmente dá exception maluco que demora dias para saber o que é , e depois corrigir. É só uma questão de fazer direito e ser previdente. se existe uma regra, porque não verificá-la ? É simples. É automático. É barato. Então proque não fazer ? Vc pelos vistos confia muito nos programadores. Mas na vida real os programadores falam mais que o código.

todo o sistema deve ser confiável. E um sistema que faz cagada sem avisar não e´confiável. Eu prefiro que ele me avise e avise o usuário, do que se cale e corrompa os dados ou dê uma mensagem do tipo “contate o administrador”. É menos stressante e quando o usuario vê uma mensagem agradável ele não pensa que o sistema foi mal feito. E isso é qualidade.

J

É claro que confio nos meus programadores… quem cria seu código afinal, maker? lol

J

sergiotaborda:

todo o sistema deve ser confiável. E um sistema que faz cagada sem avisar não e´confiável. Eu prefiro que ele me avise e avise o usuário, do que se cale e corrompa os dados ou dê uma mensagem do tipo “contate o administrador”. É menos stressante e quando o usuario vê uma mensagem agradável ele não pensa que o sistema foi mal feito. E isso é qualidade.

não fazer validação no DAO != fazer validação nenhuma

A discussão é sobre fazer validação no DAO, algo que pra mim é desnecessário se o modelo de objetos está sempre consistente com as regras do projeto.

S

JoseIgnacio:
sergiotaborda:

todo o sistema deve ser confiável. E um sistema que faz cagada sem avisar não e´confiável. Eu prefiro que ele me avise e avise o usuário, do que se cale e corrompa os dados ou dê uma mensagem do tipo “contate o administrador”. É menos stressante e quando o usuario vê uma mensagem agradável ele não pensa que o sistema foi mal feito. E isso é qualidade.

não fazer validação no DAO != fazer validação nenhuma

A discussão é sobre fazer validação no DAO, algo que pra mim é desnecessário se o modelo de objetos está sempre consistente com as regras do projeto.

Modelo de objetos não é a mesma coisa que modelo de dados. O modelo de objetos diz que o nome tem que ser um string. Mas seu eu passar @@!@$@#$@#$ como nome, obviamente não é o nome de ninguém. O mesmo se eu passar 31/2/2012. É uma data, mas não é uma data real. Portanto a verificação é muito importante. E sim, esta verificação não é feita no DAO.
A verificação do DAO é igual à do banco de dados. Se o campo não nulo, se tem o tamanho certo (tipo, o nome não é vazio e não é maior que 100 caracteres). Porque não deixar o banco fazer isto ? Primeiro porque se o encasulamento é bom eu nem sei se o banco faz isto. Aliás nem sei se existe um banco (por exemplo usar jpa no google app -engine). Segundo porque eu quero fail-fast. Porque perder tempo usando o protocolo de rede com o banco de dados só para ele me dizer que estou enviando dados errados ?

Pode parece inutil , mas não é. E ainda tem o problema de alguns bancos de dados dão uma mensagens nada a ver e vc fica perdido. No DAO vc mesmo crias as mensagens e testa as coias, então é muito mais simples resolver problemas.

J

sergiotaborda:

Modelo de objetos não é a mesma coisa que modelo de dados. O modelo de objetos diz que o nome tem que ser um string. Mas seu eu passar @@!@$@#$@#$ como nome, obviamente não é o nome de ninguém. O mesmo se eu passar 31/2/2012. É uma data, mas não é uma data real. Portanto a verificação é muito importante. E sim, esta verificação não é feita no DAO.
A verificação do DAO é igual à do banco de dados. Se o campo não nulo, se tem o tamanho certo (tipo, o nome não é vazio e não é maior que 100 caracteres). Porque não deixar o banco fazer isto ? Primeiro porque se o encasulamento é bom eu nem sei se o banco faz isto. Aliás nem sei se existe um banco (por exemplo usar jpa no google app -engine). Segundo porque eu quero fail-fast. Porque perder tempo usando o protocolo de rede com o banco de dados só para ele me dizer que estou enviando dados errados ?

Pode parece inutil , mas não é. E ainda tem o problema de alguns bancos de dados dão uma mensagens nada a ver e vc fica perdido. No DAO vc mesmo crias as mensagens e testa as coias, então é muito mais simples resolver problemas.

Mas não é de objetos que estamos falando, já que o banco não faz nenhuma validação?

Se estiver falando de validações no banco, não tenho uma opinião formada sobre o assunto, mas não me parece simples ter que validar todo o schema em código e nem vejo muito sentido nisso.

Também não tenho idéia como faria validação sem fazer qualquer afirmação sobre a natureza do armazenamento. Teoricamente, um banco de dados com espaço infinito não faria mais sentido validar pelo tamanho, por exemplo.

E pra finalizar, 31/2/2012 não é uma data em Java.

S

JoseIgnacio:
sergiotaborda:

Modelo de objetos não é a mesma coisa que modelo de dados. O modelo de objetos diz que o nome tem que ser um string. Mas seu eu passar @@!@$@#$@#$ como nome, obviamente não é o nome de ninguém. O mesmo se eu passar 31/2/2012. É uma data, mas não é uma data real. Portanto a verificação é muito importante. E sim, esta verificação não é feita no DAO.
A verificação do DAO é igual à do banco de dados. Se o campo não nulo, se tem o tamanho certo (tipo, o nome não é vazio e não é maior que 100 caracteres). Porque não deixar o banco fazer isto ? Primeiro porque se o encasulamento é bom eu nem sei se o banco faz isto. Aliás nem sei se existe um banco (por exemplo usar jpa no google app -engine). Segundo porque eu quero fail-fast. Porque perder tempo usando o protocolo de rede com o banco de dados só para ele me dizer que estou enviando dados errados ?

Pode parece inutil , mas não é. E ainda tem o problema de alguns bancos de dados dão uma mensagens nada a ver e vc fica perdido. No DAO vc mesmo crias as mensagens e testa as coias, então é muito mais simples resolver problemas.

Mas não é de objetos que estamos falando, já que o banco não faz nenhuma validação?

O banco sempre faz alguma validação porque ele tambem tem um modelo lá que vc usou para criá-lo ( em um linguagens de definição qualquer. Claro que vc pode por tudo varchar e não se preocupar com o tamanho, por exemplo. Mas isso não é um cenários em algumas empresas em que um DBA chato fica ponto char(10000) em vez de varchar)

Ora ai está. Então o DAO que usa esse banco não faria essa validação, mas o DAO que usa outro sim faria. Mas o modelo tem a informação lá, que o tamanho deve ser no máximo 100. E isto afeta até o input do usuário. Se o sistema é bom, ele vai travar além do centesimo caracter. então, aquilo que disse antes. O modelo é uma coisa, como se usa é outra. Meta-infomação é sempre bom porque vc pode decidir usá-la ou não conforme as circusntancias. Mas vc sempre tem que garantir que o sistema é coerente, que não currompe os dados. E é isso que o nosso amigo está tentando, e eu acho que vaz muito bem tentar. Já vi muito sistema sem validação apropriada e é um caos.


E pra finalizar, 31/2/2012 não é uma data em Java.

É se usar Calendar.setLenient(true). Ele aceita mesmo datas que não existem. :slight_smile:
Exatamente para vc pode validar depois.

J

Não lembro a última vez que tive que limitar o tamanho de nada. Espaço de armazenamento hoje é praticamente grátis.

Por outro lado, tempo do programador é cada vez mais escasso.

Poderia citar outro tipo de validação útil que o banco faz?

S

JoseIgnacio:
Não lembro a última vez que tive que limitar o tamanho de nada. Espaço de armazenamento hoje é praticamente grátis.

Por outro lado, tempo do programador é cada vez mais escasso.

Poderia citar outro tipo de validação útil que o banco faz?

Esse é o ponto. A validação do banco é irrelevante. O Dao faz a validação que interessa ao sistema.
Se e quando o banco fizer alguma validação (tamanho, relacionamento, null , tipagem, etc…) ela será um espelho da que o DAO faz. E se isso acontecer é porque o banco é compatilhado ( o que é comum no setor financeiro onde muitas as integrações são feitas no nivel do banco em vez de no sistema). Vc diz que o banco não precisa fazer validaçao. concordo. Mas isso apenas se e só se o DAO fizer. Caso contrário a porta está aberta para corromper dados.

A mensagem é que a validação em sistema (em objetos) deve ser feita. Se vc não quer usar validação no DAO não use. Mas pelo menos a validação de Service tem que ser feita. Ou vai me dizer que seus sistemas não têm validação nenhuma ? Não , né ?!

C

sergiotaborda e demais pessoas que quiserem compartilhar,

Diante da discussão (muito produtiva, por sinal), venho com a seguinte questão:

Suponha que em um sistema financeiro exista a classe NotaFiscal , NotaFiscalService e NotaFiscalDAO.

NotaFiscalService.salva(NotaFiscal f) recebe uma NotaFiscal para salvá-la no sistema. Nesse método são feitas MUITAS verificações para saber se a nota é válida, se já existe no sistema (evitar duplicidade), e etc etc etc, e depois se tudo passar na verificação, ela chama o método NotaFiscalDAO.salva(NotaFiscal f) para persistí-la no banco.

Agora vamos as dúvidas:


1ª Dúvida - QUAL DESSAS ABORDAGEM É A MAIS CORRETA:

  • toda a validação no Service.
  • validação de regras de negócio no Service e “validação de dados” (notnull, apenas numeros positivos e etc etc… não sei o nome que se dá a esse tipo de validação) na classe DAO.
  • toda a validação no DAO (o que pelo que vi, parece não ser uma boa idéia)

2ª Dúvida - PROBLEMA QUE VEJO NAS ABORDAGENS

  • se toda a validação for no Service, o sistema permite que uma nota fiscal seja criada e passada diretamente para o DAO, ao invés, do Service, causando um grande problema de consistencia de dados.
  • se apenas a “validação de dados” (notnull, apenas numeros positivos e etc etc) for feita no DAO, o sistema permite que uma nota fiscal seja criada e passada diretamente para o DAO, não verificando as regras de negócios e causando problemas de consistencia.
  • se toda a validação for feita no DAO, garantimos a consistencia dos dados, porém ao meu ver, não faz sentido a classe DAO executar validações de regras de negócio.

Gostaria de saber a opinião de vocês nessas abordagens para tentar chegar em uma possível “forma ideal”. Concordo em parte quando dizem que não da pra fazer um super sistema invencível a tolos em prazos apertados. Mas também acho que podemos garantir um bom nível de consistencia de dados.
Fico no aguardo dos mais experientes!

S

CodeDeveloper:
sergiotaborda e demais pessoas que quiserem compartilhar,

Diante da discussão (muito produtiva, por sinal), venho com a seguinte questão:

Suponha que em um sistema financeiro exista a classe NotaFiscal , NotaFiscalService e NotaFiscalDAO.

NotaFiscalService.salva(NotaFiscal f) recebe uma NotaFiscal para salvá-la no sistema. Nesse método são feitas MUITAS verificações para saber se a nota é válida, se já existe no sistema (evitar duplicidade), e etc etc etc, e depois se tudo passar na verificação, ela chama o método NotaFiscalDAO.salva(NotaFiscal f) para persistí-la no banco.

Agora vamos as dúvidas:


1ª Dúvida - QUAL DESSAS ABORDAGEM É A MAIS CORRETA:

  • toda a validação no Service.
  • validação de regras de negócio no Service e “validação de dados” (notnull, apenas numeros positivos e etc etc… não sei o nome que se dá a esse tipo de validação) na classe DAO.
  • toda a validação no DAO (o que pelo que vi, parece não ser uma boa idéia)

2ª Dúvida - PROBLEMA QUE VEJO NAS ABORDAGENS

  • se toda a validação for no Service, o sistema permite que uma nota fiscal seja criada e passada diretamente para o DAO, ao invés, do Service, causando um grande problema de consistencia de dados.
  • se apenas a “validação de dados” (notnull, apenas numeros positivos e etc etc) for feita no DAO, o sistema permite que uma nota fiscal seja criada e passada diretamente para o DAO, não verificando as regras de negócios e causando problemas de consistencia.
  • se toda a validação for feita no DAO, garantimos a consistencia dos dados, porém ao meu ver, não faz sentido a classe DAO executar validações de regras de negócio.

Gostaria de saber a opinião de vocês nessas abordagens para tentar chegar em uma possível “forma ideal”. Concordo em parte quando dizem que não da pra fazer um super sistema invencível a tolos em prazos apertados. Mas também acho que podemos garantir um bom nível de consistencia de dados.
Fico no aguardo dos mais experientes!

O que vc deve fazer é criar um NotFscalValidator que contém as regras. Depois o NotaFiscalService vai simplesmente invocar o validator.
O seu problema de algum desavisado chamar o DAO diretamente sem passar pelo service e por isso tenho que validar no DAO não procede. Se o cara programou errado, problema dele.
Do ponto de vista do design o DAO não é um objeto de dominio e não pode saber das regras. Aliás, em algumas circusntancias vc não vai querer esa validação. O serviço que o DAO provê é simples Acesso (Data Acess Object). Regras não é com ele.
E como já discutimos antes, a validação feita no DAO (null etc … ) é apenas para não sobrecarregar o banco de dados e a rede.

A separação de camadas tem precedencia sobre “programadores desavisados”.E na prática, o DAO vai salvar em varias circusntancias, não apenas no create , por isso ele não deve validar demais. O service por sua vez, pode pegar uma nota e processaá-la de diferentes formas e salvar no final. Cada forma terá sua validação. Entao é o método do service que escolhe o validator, o validator que valida, e o DAO simplesmente grava.

Criado 13 de dezembro de 2012
Ultima resposta 15 de jan. de 2013
Respostas 30
Participantes 6