VRaptor: Duvida sobre arquitetura

14 respostas
V

Tudo bem pessoal?

Estava olhando o exemplo mydvds-2.5.0 do VRaptor e percebi na classe LoginComponent que o VRaptor usa o método validateExecute para validar se o usuário/senha estão corretos. O problema é que esse método recebe ValidationErrors como parametro, que eh uma classe do VRaptor.

Dúvida: não estamos assim acoplando o modelo de negócios ao VRaptor? Seria isso um problema ou é uma frescura minha? Nesse caso, quando eu tenho que ir no banco de dados, validar o usuário/senha e retornar uma mensagem de erro caso a senha esteja errada, não seria melhor ter uma action separada (LoginAction) do modelo de negócios (LoginComponent) de forma que eu possa fazer essa validação de dentro da minha action usando o LoginComponent?

Sou a favor de evitar o uso de uma action, mas nesse caso fiquei pensando se não seria melhor deixar o LoginComponent livre de qualquer acoplamento com o VRaptor e fazer essa validação+mensagens numa action ou algo assim.

Qual é a opinião do pessoal do framework?

Valeuz!

Rodrigo

14 Respostas

Fabio_Kung

Oi vraptado,

Infelizmente a resposta para a sua pergunta é um decepcionante: depende.

Não há respostas certas para perguntas como esta. Vai depender de cada caso. Eu tenho a tendência atual de seguir o lado mais pragmático e evitar burocracia desnecessária.

Você vai precisar reaproveitar essa lógica de autenticação/autorização em algum outro lugar fora do ambiente web e fora desse seu sistema? A lógica de autenticação/autorização faz parte do seu domínio?

Só isolaria essa lógica do LoginComponent em uma classe de domínio se a resposta para as duas perguntas fosse afirmativa. Pelo menos pela minha experiência, quase sempre as duas (ou pelo menos uma delas) é negativa, já que autenticação e autorização são geralmente infra-estrutura e não parte do domínio. Além disso, na prática eu nunca vi esse tipo de lógica/comportamento ser reaproveitado entre dois sistemas diferentes.

Porém, pode acontecer. A decisão de separar ou não é sua. O importante é você saber justificar bem as suas razões e saber as consequências dela. O VRaptor, pelo menos, não entra no seu caminho nesse aspecto.

edit: portugues…

V

Não só no LoginComponent, mas em muitas outras situações você pode precisar adicionar uma mensagem de erro para ser exibida na camada de apresentação.

Como estou vindo do Struts, minha dúvida é o seguinte: Ou você se preocupa com o acoplamento ou você não se preocupa com o acoplamento.

Evitando a burocracia, como você falou, eu poderia estender ActionSupport e sair fazendo tudo (acessar DAO, etc) dentro da minha action. Como eu estou estendendo ActionSupport, o Struts já vai me fornecer diversos métodos para fazer as coisas do framework. Mas isso me parece muito intrusivo, por isso eu sempre tive o costume de usar o esquema de action -> modelo, isto é, eu separo minha requisição em dois passos (duas classes) action e modelo (facade).

Fiquei agora com essa dúvida conceitual. Qual a vantagem do VRaptor se a minha action vai ficar atrelada da mesma maneira que ela fica com o Struts?

Editado:

Outra dúvida: Como estou vindo do struts, estou acostumado a retornar um resultado para indicar para que página eu quero ir. Vi que o VRaptor também suporta isso. O meu problema é que retornar uma String do modelo de dados é sem-sentido. Isso é coisa de action e não de modelo de dados, concorda? Estou com um caso trivial, um método loadUser que recebe um id. Se o usuário não existir no banco de dados, deve ser adicionado uma mensagem de erro e jogar o cara para uma página especial, ou seja, eu TENHO que retornar outro resultado. Mas ficar returnando “ok” e "error"do meu modelo de negócios me parece muito esquisito.

O que vocês recomendam para contorar esse problema? Será que o modelo sem action é possível ou vamos sempre acabar misturando tudo numa classe só como parece que o VRaptor faz…

Editado:

Outra dúvida: Vi que para a validação o VRaptor procura automaticamente por validateXXXX para validar o método XXXX. Isso é legal, mas porque nao implementar uma interface para fazer isso, assim como o Struts2 tem o Validatable? Se já estou acoplado ao objeto ValidationErrors, qual seria o mal de fazer minha action implementar uma interface. Eu prefiro interface porque o código fica mais sólido. Sem interface, se você erra a definicao do método nao funciona.

O que vocês acham de uma action do VRaptor implementar uma interface? Isso tem alguma desvantagem?

Paulo_Silveira

Ola (nao sei seu nome)

Voce tem razao nos seus pontos, e essa sempre foi uma discussao muito grande entre o pessoal: o ValidationErrors é um unico import (sem ser anotacoes) que fazemos com o componente do VRaptor. Essa parte de validacao do VRaptor precisa ser retrabalhada para ficar mais bonita e desacoplada.

Sobre retornar String, a outra opcao que tinhamos era assim: os metodos deveriam ser void e a resposta padrao seria “ok”, e caso voce precise ir para outra pagina, deveria lancar uma exception, que atraves dela saberiamos para onde ir. O que acha? Eu fui vencido em relacao a essa opcao :slight_smile:

Esses problemas tambem aparecem no JBoss Seam: acabamos as vezes usando FacesContext no meio do nosso “POJO” e retornando Strings, e em diversos outros frameworks em que a proposta é pular a Action diretamente para a camada de negocios.

Hoje tambem acho que o desacoplamento 100% do framework esta cada vez mais dificil, entao os objetivos dos framework web devem ser 1-) abstrair ao maximo as dificuldades do pacote javax.servlet e 2-) fazer com que a classe seja a mais simples possiveil, facilitando testes unitarios e diminuindo suas dependencias. Depender de Strings, como voce disse no valdiateXXX, é realmente um contrato fraco, assim como usar webservices POX ou JSON.

Voce tem algumas sugestoes? Temos algumas ideias ja para o VRaptor 3 para quando sair com o Java 7 SE, adoraria ouvir opinioes de alguem que esta estudando a fundo o framework como voce.

Sobre sua pergunta de interfaces, acho que vale a pena sim uma abordagem dessa maneira, mas prefiro um @ValidationMethod(method=“comprar”) ou algo assim, ja que se fosse pela interface teriamos de receber algo como Method como argumento.

Guilherme_Silveira

Ola Rodrigo tudo bem com voce?

Que bom que baixou o projeto e esta se divertindo com ele… é muito importante para um desenvolvedor conhecer as ferramentas que uma tecnologia disponibiliza e não viciar somente em uma, uma vez que não existe bala de prata

O pessoal aqui já respondeu um pouco suas dúvidas, mas acho um ponto legal de comentar.

Antigamente (pensamento da epoca do j2ee) o pessoal acreditava piamente em separar a Action (do struts, por exemplo) da logica (em um ejb por exemplo) para “abstrair” a parte web… e vimos projetos e mais projetos de pequeno e medio porte com uma complexidade muito alta somente para abstrair algo que acabou custando muito para eles: um desenvolvedor que conhece mais tecnologias, que é mais caro e que nem sempre conhece direito a teoria por tras daquilo que esta usando (chamadas remotas e suas implicacoes)

Sempre que faço um papel mais alto nivel, tento recomendar fugir desse tipo de arquitetura, onde voce tenta separar cada vez mais coisas… ganhando cada vez MAIS complexidade no seu sistema.

Mas claro, é sempre questão de medir.

Ah… vale lembrar que se comunicar com interfaces é sempre melhor do que com classes (ValidationErrors eh interface, mas a mensagem eh uma classe). O legal disso é que diferentes bibliotecas podem utilizar isso para voce (como é oc aso do vraptor nesse exemplo ou a taglib w)

Abraco

Emerson_Macedo

Eu usei o vraptor em um projeto real que foi pra produção. Gostei bastante da simplicitade. Gostaria até de saber como é o procedimento para se tornar um commiter, pois acredito no framework para aplicações Web Java (Não quero ninguém tacando nessa thread JRuby on Rais por favor :smiley: )

V

Eu acho assim: Ou você mistura action + web + modelo num mesmo lugar OU você separa (action + web) do modelo. Se for para acoplar só um pouquinho, melhor acoplar tudo logo e cortar a burocracia. Se for para manter o modelo desacoplado e bonito, então melhor deixá-lo realmente desacoplado.

O problema está no detalhes. Manter o modelo totalmente desacoplado e sem action é muitas vezes complicado porque temos que ir para paginas diferentes, temos que colocar mensagens de erros, etc. Outro problema é quando for necessário trabalhar com a session. O que faremos aqui? Injetaremos a session dentro do modelo?

O SEAM tem esse mesmo problema, acha que tudo pode ser injetado no facade como se não houvesse a camada web. Não acredito como o Guilherme falou que fica mais complexo apenas porque tem mais uma classe (action) antes do modelo. Na verdade o cara tem que escolher: ou ele vai ignorar a teoria e a burocracia e colocar tudo no modelo OU ele vai fazer as coisas organizadas separando modelo de (action + web). Eu acho que vale a pena, pois apesar de uma classe a mais a arquitetura fica bem separadinha e bonitinha e você escapa desses problemas de o modelo fazer validacao, retornar resultados para paginas diferentes, trabalhar com session, etc.

Acho que isso dá para fazer com o VRaptor. A minha únida dúvida é se o VRaptor não focou muito na parte do modelo+action desacoplado e para codificar apenas a action não seria mais difícil. Eu gosto de como o Struts2 faz, de te permitir herdar de ActionSupport para a action. O que vocês acham disso?

Paulo_Silveira

Oi

Eu acho que podemos acoplar mais sim, porem se voce acoplar muito, da quase na mesma que usar HttpServlet direto!!!

Nao gosto da ideia de herdar de classe nenhuma, herdar é a acoplagem na certa. Seria muito mais facil, em vez de voce estender ActionSupport, voce receber, injetado pelo construtor or por parametro do metodo, esse mesmo ActionSupport (so que interfaceado). Exemplo

class MinhaAction (talvez implements BlaBla) {

   public MinhaAction(ActionSupport support) {
     this.support = support;
   }
   
   void logicaDeNegocio(String parametroDoForm, Calendar dateDoForm) {
     support.adicionaMensagemDeErro("login invalido");
     support.redirecionaPara("paginaDeAutenticacao")
   }
}

Essa classe da para fazer um belo de um unit test. Se voce estender ActionSupport, simplesmente nao da, porque nao tem como voce mockea-la! Eu queria fazer isso no VRaptor, ter um objeto unico e “magico”, que trabalha com validacao, controle de erro, redirecionamento e outros detalhes que as vezes misturamos no meio da logica de negocio.

V

Injectar o ActionSupport com tudo da action é legal mas acopla da mesma maneira que acopla quando voce herda de ActionSupport. E para testar também teremos o mesmo problema, ou seja, precisaremos de um mock para o ActionSupport.

Qual o problema, além de teste unitário, você vê para a herança de um ActionSupport? Para teste eu não vejo problema, basta ter uma MockActionSupport.

UserAction action = new UserAction();
MockActionSupport mock = new MockActionSupport(action);
action.setUsername("rodrigo");
String result = mockAction.add();
assertEqual(result, "ok");
result = mockAction.add("rodrigo");
assertEqual(result, "ok");
Paulo_Silveira

Sobre estender uma classe apenas por conveniencia, isso ´um topico ja amplamente debatido, e ate o Eric Gamma fala, prefira composicao em vez de heranca. Voce tambem encontra James Gosling, Joshua Bloch, Bruce Eckel e Martin Fowler falando mal de heranca.

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

Eu escrevi um sumario aqui:

http://blog.caelum.com.br/2006/10/14/como-nao-aprender-orientacao-a-objetos-heranca/

Alem disso, seu unit test vai ser trabalhoso. Se sua action chamar um metodo dela mesma, a sua classe Mock nao vai receber a invocacao, e voce nao vai ter como verificar essa requisicao la. Pior ainda, essa classe ActionSupport vai ter um metodo invocado (como por exemplo pegar a HttpServletRequest), como voce vai fazer para resolver isso? Como vai interceptar isso? Como registrar que esse metodo foi invocado N vezes? Ja que voce estende dessa classe, fica impossibilitado de fazer essa interceptacao para poder fazer uma mockagem. Nao sei se expliquei bem, mas voce nao consegue mesmo testa-la sem um maior esforco. No caso de receber injetado, usar jmock ou algo do genero ja auxilia muito.

Concordo que o resultado para o usuario final seja parecido, mas nao gosto mesmo da heranca. (voce mesmo disse que acha mais bonito e desacoplado interfaces).

Mas considerando tanto a heranca ou a injecao de um objeto magico que te auxilia muito, voce ainda iria quebrar em action+dominio ou deixar misturado como os controllers do ruby? (eu ando com essa duvida)

Luca

Olá

Paulo, parabéns pela paciência das suas respostas.

Sempre que vejo anônimos escondidos com IP anônimos perco 99% da vontade de responder. Porque alguém precisaria se esconder com IP anônimo para postar no GUJ?

[]s
Luca

Paulo_Silveira

O mais curioso é que as perguntas e criticas sao sensatas, educadas e construtivas.

V

Eu prefiro separar o papel da action e das operações relacionadas a camada web. Por isso uma classe para a action e outra para o modelo/facade.

Uso um proxy aqui na empresa, deve ser isso. :wink:

pcalcado

Apesar da Action não fazer parte do Model (e da Camada de Negócios) você doe utilizá-la como Camada de Aplicação. Num projeto com pouca lógica e apenas uma interface web esse é um ótimo cenário.

Guilherme_Silveira

Gosto dessa visão do Phillip…

E ai alguns caras (controladores) podem disponibilizar essa sua mesma logica para webservices por exemplo, sem voce mudar (quase) nada… (@Webservice do ejb ou @Remotable do vraptor por exemplo)…

Criado 30 de janeiro de 2008
Ultima resposta 31 de jan. de 2008
Respostas 14
Participantes 7