Exceções no MVC

Pesquisei por tópicos relacionados, mas não achei nenhum que respondesse minha dúvida.
Estou iniciando um projeto com o VRaptor, e estou com uma dúvida:
Caso, por algum motivo qualquer, ocorra um excessão no meu modelo, essa excessão tem de ser repassada até o controller para ele se encarregar de mostrar um página personalizada com o erro?

Vamos supor o seguinte exemplo:

public class UserDao {
   public void add(User user) throws MinhaException {
      //...
   }
}
public class UserService {

   private UserDao dao;

   public void add(User user) /*aqui teria que ter um throws MinhaException tbm?*/ {
      //realiza alguma logica...
      dao.add(user);
   }
}

public class UserController {
   private UserService service;
   
   public void add(User user) {
      //aqui teriamos que tratar a excessão explicitamente, certo?
   }
}

Esse seria +/- meu modelo, gostaria de sugestões e esclarecimentos :slight_smile:

Não conheço o vraptor, mas acho que desde os servlets, já era possível configurar um comportamento default da aplicação via container no caso de levantamento de exceções, então é possível que o vraptor tenha algo pronto neste sentido tb.

Do ponto de vista lógico, eu imagino que seja sempre bom vc explicitar as exceções nas assinaturas dos métodos e tentar tratar, sim, na camada de controle. Vc pode inclusive achar bem útil criar uma classe só para isso.

Para checar se sua estrutura MVC está boa, imagine como vc faria pra implementar sua aplicação, que hoje é web, como uma interface linha de comando, e checar o quanto de código vc consegue reaproveitar. Nada como uma restrição real para validar a utilidade da teoria :wink:

[]

Leo K.

Algo parecido…

[quote]Para onde redirecionar no caso de erro
Outro ponto importante que deve ser levado em consideração no momento de fazer validações é o redirecionamento quando ocorrer um erro. Como enviamos o usuário para outro recurso com o VRaptor3, caso haja erro na validação? Simples, apenas diga no seu código que quando correr um erro, é para o usuário ser enviado para algum recurso. Como no exemplo:
public adiciona(Funcionario funcionario) {
//Validação na forma fluente
validator.checking(new Validations(){{
that(“erro”,“nomeNaoInformado”, !funcionario.getNome().isEmpty());
}});
//Validação na forma clássica
if(! funcionario.getNome().equals(“Fulano”)) {
validator.add(new ValidationMessage(“erro”,“nomeInvalido”));
}
validator.onErrorUse(page()).of(FuncionarioController.class).formulario();

dao.adiciona(funcionario);

}
Note que se sua lógica adiciona algum erro de validação você precisa dizer pra onde o VRaptor deve ir. O validator.onErrorUse funciona do mesmo jeito que o result.use: você pode usar qualquer view da classe Results.[/quote]

vide http://vraptor.caelum.com.br/documentacao/validacao/

Tudo bem, Shikida?

Porque voce acha que e melhor tratar na camada de controle? O certo nao seria restringir as excecoes no Model com seus requests propriamente certos para cada tipo de eventualidade e deixar para tratamento do Controller apenas os erros nao previstos, principalmente os que derivam de Error?

Oi Jaba

acho que não entendi sua colocação.

vc poderia dar um exemplo de código do modo como vc acha que seria melhor?

eu tendo a preferir tratar na camada de controle pq eu penso na camada de modelo não em termos de DAOs, mas de entity beans. E neste caso, entity beans não costumam possuir regras de negócios, no máximo alguns métodos utilitários simples e fáceis de blindar (por exemplo, algum método que retorne um booleano conforme uma combinação de valores do estado daquele bean, de função puramente semântica).

claro que DAOs são persistência e ficam muito mais perto da camada de modelo, mas como hoje em dia a gerência de persistência é feita por componentes maduros, eu acho mais provável que haja maior necessidade de tratar exceções fora desta camada. Dificilmente, hoje em dia, eu teria que tratar por exemplo a liberação explícita de conexões JDBC mortas, como se fazia 10 anos atrás, pq algum jpa/hibernate/toplink da vida vai tratar isso para mim. É bem mais fácil estabilizar o código do modelo.

Centralizar o tratamento de exceções na camada de controle, para mim, é, portanto, mais uma questão de comodidade.

[]

Leo K.

Seu modelo deve disparar exceções sempre que alguma regra do negócio for violada. E prefira exceções não checadas - que herdem de RuntimeException - para não ter que ficar declarando as exceções lançadas por um método em sua assinatura.

Dito isso, eu concordo com o shikida. As exceções devem ser tratadas adequadamente pelo controle. Uma mensagem pode, por exemplo, ser exibida ao usuário, ou ele pode ser redirecionado a uma página de erro, etc.

Isso depende do que queremos dizer com tratar a exceção? Se for apenas mostrar uma tela de erro, sem dúvida alguma essa exceção pode ser apenas tratada na camada de controle. Se, por outro lado, a exceção exige ações corretivas mais complexas, como: mandar um e-mail, reagendar um processo, manipular algum arquivo, etc. ainda seria adequado tratar isso apenas na camada de controle?

Pelo o que voce falou, shikida, voce joga TODAS as suas excecoes para o controller, certo?
Mas eu acho que fazer isso e delegar muita responsabilidade, existem muitos problemas no Model que podem ser lancados devivo a regras de negocio, ou ate mesmo excecoes nao verificadas que escapam, e deixando tudo no controller, voce perde o controle de manipulacao de muitas dessas excecoes.

Oi Jaba

qual seria uma exceção que vc trataria na camada de modelo? Um nullpointer vá lá.

o esmiralha por exemplo, falou

minha resposta: você faria uma chamada a um método que envia um email (regra de negócio) ou reagenda um processo (regra de negócio) etc na camada de modelo? :smiley:

[]

Leo K.

Nesse caso, podemos usar a camada de aplicação, definida pelo Eric Evans como uma fina camada entre a UI e o modelo do domínio.

Imagine o seguinte exemplo: em nossa aplicação é possível que um usuário qualquer modifique sua senha. No momento em que ele informa a nova senha, precisa digitá-la novamente para confirmar. Vamos imaginar, só para fins ilustrativos, que há um requisito que diz que é preciso enviar um e-mail para o administrador do sistema caso haja algum erro na confirmação da senha.

Em código, seria algo como:

[code]// A classe usuário faz parte do modelo.
class Usuario {
// Atributos omitidos

// Só é possível mudar a senha caso a nova senha e a confirmação sejam iguais.
public void mudarSenha(String novaSenha, String confirmacaoSenha) {
    if (!novaSenha.equals(confirmacaoSenha) {
        throw new ConfirmacaoSenhaException("As senhas informadas não são iguais.");
    }

    this.senha = novaSenha;
}

}

// A classe UsuarioService faz parte da camada de aplicação. Ela é utilizada diretamente pelo controle.
class UsuarioService {
// Atributos omitidos.

public void mudarSenha(String login, String novaSenha, String confirmacaoSenha) {
    Usuario u = usuarioRepository.get(login);

    try {
        u.mudarSenha(novaSenha, confirmacaoSenha);
    } catch (ConfirmacaoSenhaException ex) {
        // Mandar e-mail...
        MailService.send();
        // .. e relançar a exceção para o controle tratar.
        throw ex;
    }
}

}[/code]

A tarefa de enviar e-mail poderia ser substituída por qualquer uma das que você falou.

Eu acho a solução do Tarso a mais correta. Vc lança a exceção na camada de modelo mas trata na camada de negócios.

Mas eu se eu precisasse exibir o erro pro usuário?

PS: O Service não fica na camada de modelo? Sendo que ele vai ditar as regras da aplicação?

Observe que a classe UsuarioService trata a exceção, envia o e-mail e relança a exceção para o código que está utilizando a classe, nesse caso o controle. Aí então o controle pode fazer o que quiser - exibir o erro e avisar que um e-mail foi enviado para o administrador do sistema, por exemplo.

O service, no exemplo que eu citei, fica na camada de aplicação.

Em anexo há uma imagem da arquitetura em camadas proposta por Eric Evans, extraída do livro Domain-Driven Design Quickly.


Observe que a classe UsuarioService trata a exceção, envia o e-mail e relança a exceção para o código que está utilizando a classe, nesse caso o controle. Aí então o controle pode fazer o que quiser - exibir o erro e avisar que um e-mail foi enviado para o administrador do sistema, por exemplo.

O service, no exemplo que eu citei, fica na camada de aplicação.

Em anexo há uma imagem da arquitetura em camadas proposta por Eric Evans, extraída do livro Domain-Driven Design Quickly.

[/quote]

Desculpe, não reparei, falha minha.

Sobre o Service, o Controller ficaria em que camada então?
PS: Mas essas camadas não são diferentes da proposta do MVC?

[quote=j0nny]Sobre o Service, o Controller ficaria em que camada então?
PS: Mas essas camadas não são diferentes da proposta do MVC?[/quote]
O controle fica na camada de apresentação. Na figura, essa camada é representada pela legenda User Interface.
MVC não são camadas. A visão e o controle ficam na camada de apresentação; o modelo do MVC é todo o resto.

[quote=tnaires][quote=j0nny]Sobre o Service, o Controller ficaria em que camada então?
PS: Mas essas camadas não são diferentes da proposta do MVC?[/quote]
O controle fica na camada de apresentação. Na figura, essa camada é representada pela legenda User Interface.
MVC não são camadas. A visão e o controle ficam na camada de apresentação; o modelo do MVC é todo o resto.[/quote]

Mas a apresentação não seria o “V” do MVC?

Ainda segundo o livro Domain-Driven Design Quickly, a camada de apresentação é “responsável por apresentar informação ao usuário e interpretar os comandos de usuário” (tradução livre).

Inferimos, portanto, que a visão e o controle, segundo Eric Evans, pertencem à camada de apresentação, uma vez que quem exibe os dados para o usuário é a visão e quem interpreta os comandos do usuário é o controle.

Ainda segundo o livro Domain-Driven Design Quickly, a camada de apresentação é “responsável por apresentar informação ao usuário e interpretar os comandos de usuário” (tradução livre).

Inferimos, portanto, que a visão e o controle, segundo Eric Evans, pertencem à camada de apresentação, uma vez que quem exibe os dados para o usuário é a visão e quem interpreta os comandos do usuário é o controle.[/quote]

Blza, mas ficou meio confuso essa imagem, vamos ver se entendi.
User Interface - HTML (por exemplo) e Controller
Application - Service
Domain - Entidades
Infraestrutura - DAO’s,
certo?

[quote=j0nny]Blza, mas ficou meio confuso essa imagem, vamos ver se entendi.
User Interface - HTML (por exemplo) e Controller
Application - Service
Domain - Entidades
Infraestrutura - DAO’s,
certo?[/quote]
A infraestrutura fornece todos os serviços utilizados por todas as outras camadas. A persistência é apenas um desses serviços; o código que envia e-mail também é um serviço de infraestrutura.

Não sei se eu entendi bem esse esquema da arquitetura do Evans. Dando uma olhada nas wikipedias da vida, parece que a proposta dele é oferecer uma série de práticas que ajudam a modelar sistemas cuja lógica de negocios é muito complicada, cooperando com especialistas de domínio e tentando compartimentar as idiossincrasias que surgem naturalmente na lógica de negócios deste tipo de sistema em partes específicas do código.

Pode não ser o caso geral. E não se contrapõe ao MVC.

O que eu entendo por MVC pro mundo java (posso estar errado) é que quando vc tenta separar as funcionalidades da sua aplicação em compartimentos comuns, isto é, colocar todo mundo que faz controle num canto, todo mundo que é visão no outro, e etc que vc promove reuso e organização (leia-se facilidade de manutenção) ao seu código.

Prá mim, controle e visão são coisas que devem ficar separadas sim justamente por essa lógica de reuso. Se eu tenho uma aplicação com interface web hoje (interface web prá mim é V) e eu quero mudar para uma aplicação em flash ou uma interface em linha de comando, eu não quero ficar mexendo muito na minha camada de controle, que deve estar cheia de métodos que representam ações em cima das informações.

Por isso, se eu for tratar exceções na minha camada de modelo que mandam emails ou emitem mensagens prá serem renderizadas por um JSP ou JSF, eu estou me comprometendo a ter que tratar isto quando for a interface linha de comando que estiver emitindo os comandos.

O exemplo clássico JAVA é o JSP na view, o Servlet no controle e os beans no modelo. JSP apresenta coisas, Servlets recebem requests e devolvem responses e alguém gerencia os beans, geralmente algum gerenciador de persistência. Se eu quiser arrancar meu JSP fora e implementar uma interface linha de comando, nada impede que os Servlets continuem pegando requests (que não precisam ser Http inclusive) e devolvendo response, desde que eles não saiam gerando HTML lá dentro. Até porque, HTML, prá mim, é decisão de VIEW.

Alguns anos atrás eu tive que dar manutenção num aplicativo que pegava dados científicos, processava e visualizava numa interface desktop swing. Só que ele também tinha uma interface linha de comando onde vc submetia os arquivos e ele gerava, em batch, os arquivos de saída que normalmente seriam visualizados. Minha manutenção era justamente transformar isso numa aplicação web. ou seja, eu não queria de jeito nenhum mexer na lógica de negócios, mas não podia contar com nada que fosse específico da interface swing tb.

Neste dia eu agradeci que o sistema tinha um mínimo de separação entre as 3 coisas.

É assim que eu entendo MVC.

Outro exemplo. Suponha que vc tem uma aplicação web que tem uma tela com um botão que dispara um processo qualquer, digamos, importação ou envio de mensagem. Se algo der errado, vc quer que apareça uma mensagem na tela avisando que alguma coisa ruim aconteceu.

Agora imagine que amanhã seu gerente te pede prá adicionar uma funcionalidade ao sistema que é fazer a mesma coisa a partir de uma tarefa agendada via quartz, cron ou algo do tipo. Prá rodar headless num outro servidor. Como deve ser gerada e prá onde vai sua mensagem de erro? Provavelmente vai ter que ir prá um arquivo ou tabela de log num banco. Com um bom MVC, fica mais fácil lidar com isso.

[]

Leo K.