Arquitetura app web com SpringMVC

34 respostas
diogoj

Pessoal, estou dando meus primeiros passos com o desenvolvimento web em Java. Estou utilizando o SpringMVC, tenho algumas dúvidas e gostaria de saber se estou no caminho correto. vamos considerar a seguinte situação:

Interface ClienteDAO

package br.com.xxx.sistema.dao;

import java.util.List;

import br.com.xxx.sistema.model.Cliente;

public interface ClienteDao {

    public Cliente findById(Integer id);
    public List findAll();
}

ClienteDaoHibernate: Implementação da interface

package br.com.xxx.sistema.dao.hibernate;

import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import br.com.xxx.sistema.dao.ClienteDao;
import br.com.xxx.sistema.model.Cliente;

public class ClienteDaoHibernate extends HibernateDaoSupport implements ClienteDao {

    public Cliente findById(Integer id) {
        return null;
    }

    public List findAll() throws DataAccessException {
        Session session = getSession(true);        
        try{
            List result = session.createQuery("from Cliente").list();            
            if(result == null){
                throw new RuntimeException("ResultSet inválido.") {};
            }            
            return result;
        } catch ( HibernateException ex){
            throw convertHibernateAccessException(ex);
        }
    }
}

Controller

package br.com.xxx.sistema.web.controllers.spring;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import br.com.xxx.sistema.dao.ClienteDao;

public class ListClientesController implements Controller  {
    
    private ClienteDao clienteDao;
    
    public void setClienteDao(ClienteDao clienteDao){
        this.clienteDao = clienteDao;
    }

    public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {                
        Map model = new HashMap();
        model.put("listaClientes",clienteDao.findAll());
        return new ModelAndView("listarClientes",model);
    }
}

applicationContext.xml

[...]
	<bean id="ClienteDAO" class="br.com.xxx.sistema.dao.hibernate.ClienteDaoHibernate">
		<property name="sessionFactory" ref="mySessionFactory"/>
	</bean>

[...]

action-servlet.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

	<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
		<property name="prefix" value="/WEB-INF/jsp/"/>
		<property name="suffix" value=".jsp"/>
	</bean>

	<bean id="defaultHandlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

	<bean name="/index.html" class="org.springframework.web.servlet.mvc.ParameterizableViewController">
		<property name="viewName" value="index" />
	</bean>

	<bean name="ListClientesController" class="br.com.xxx.sistema.web.controllers.spring.ListClientesController">
		<property name="clienteDao" ref="ClienteDAO" />
	</bean>

	<bean name="EditClienteController" class="br.com.xxx.sistema.web.controllers.spring.EditClienteController">

	</bean>

    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <value>
                /content/clientes/list.html=ListClientesController
                /content/clientes/edit.html=EditClienteController
            </value>
        </property>
    </bean>

</beans>

Funcionando está, mas gostaria de saber:

Está correto?

O que pode ser melhorado?

Já li várias vezes que o controller tendo acesso direto ao DAO não está correto, no meu caso, como eu poderia fazer com que o controller nao tenha acesso direto ao ClienteDao?

Obrigado pessoal.

34 Respostas

D

Realmente, o seu controller está acessando diretamente o DAO … Mais tarde posto uma possível sugestão, pois estou sem tempo.

Pessoal ajudem nosso amigo.
Sds
e até mais

diogoj

Duke, continuo aguardando sugestões…

fviana

bom, a ideia de DAO é fazer o acesso aos dados, ou seja, execução dos sql’s…

O ideal seria vc criar um BusinessDelegate e um BusinessObject.
O BusinessObject contendo as regras de negocio e acessando o DAO.
O BusinessDelegate sendo a ligação entre o Controller e o BusinessObject.

Ex:

class ClienteBO{
  ClienteDao dao;
  public viod inserir(Cliente cliente){
    if (cliente.getDataCadastro() == null) // uma RN
      cliente.setDataCadastro(new Date());
    dao.inserir(cliente);
  }
}

class ClienteBD{
  ClienteBO bo;
  public viod inserir(Cliente cliente){
    bo.inserir(cliente);
  }
}

class ClienteController extends Controller{
  ClienteBD bd;
  public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {                
    Cliente cliente = new Cliente();
    bd.inserir(cliente);
    return new ModelAndView("confirmacao");
  }
}
saoj

Como o seu DAO vai receber uma connection com o banco ou uma session com o Hibernate ???

Um DAO precisa disso para operar, certo ?

Kenobi
fviana:
bom, a ideia de DAO é fazer o acesso aos dados, ou seja, execução dos sql's...

O ideal seria vc criar um BusinessDelegate e um BusinessObject.
O BusinessObject contendo as regras de negocio e acessando o DAO.
O BusinessDelegate sendo a ligação entre o Controller e o BusinessObject.

Ex:
class ClienteBO{
  ClienteDao dao;
  public viod inserir(Cliente cliente){
    if (cliente.getDataCadastro() == null) // uma RN
      cliente.setDataCadastro(new Date());
    dao.inserir(cliente);
  }
}

class ClienteBD{
  ClienteBO bo;
  public viod inserir(Cliente cliente){
    bo.inserir(cliente);
  }
}

class ClienteController extends Controller{
  ClienteBD bd;
  public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {                
    Cliente cliente = new Cliente();
    bd.inserir(cliente);
    return new ModelAndView("confirmacao");
  }
}

Bom existem algumas maneiras de se fazer arquitetura em cima do Spring, fazendo uso dos seus recursos benefícios.

Uma seria o ActiveRecord, bastante útil para aplicações simples, pequenas que são basicamente CRUDS.

Você poderia também criar uma interface de business, verficar se haverá padronização dos métodos, nas suas classes de negócio. Ou fazer uma chamada única, encapsulando internamente à cada uma suas trativas.

Em alguns momentos suas classes de negócio serão statless, outras stateful .

Em ambos os casos, você poderá fazer uso de injeção de dependência através de uma interface genérica - IBusiness e pra cada implementação, você avalia se haverá ou não estado e seta a propriedade singleton como true ou false.

Caso tenha estado, setando como false, você terá implementado como default o pattern prototype. Há ainda como fazê-lo de maneira explícita usando AOP, dá uma pesquisada.

:-) :twisted:

Kenobi

saoj:
Como o seu DAO vai receber uma connection com o banco ou uma session com o Hibernate ???

Um DAO precisa disso para operar, certo ?

Com o uso do Hibernate no Spring, estendendo a classe HibernateDaoSupport, você seta as configurações no seu app-servlet.xml , até mesmo fazendo uso de properties para o jdbc, deixando tudo fora da implementação.

saoj

Todos os seus Daos vão ter que extender HibernateDaoSupport ? Não é melhor fazer isso com DI ?

Mauricio_Linhares

saoj:
Todos os seus Daos vão ter que extender HibernateDaoSupport ? Não é melhor fazer isso com DI ?

Se você quizer todas as facilidades da integração do Spring com o Hibernate, tem que ser sim, o que não é nenhum problema, já que os módulos do Spring são independentes :smiley:

Mas quem quer fazer no braço, pode fazer também, só não vai ser tão simples quando poderia ser.

E essa história de controller não pode acessar o DAO é puro blablabla, não invente mais camadas nem complique mais a sua vida por causa de uma besteira dessas.

saoj

Só estou questionando para entender como funciona. Não sei se é melhor ou pior.

Eu simplesmente usaria IoC/DI para injetar uma connection/session dentro do meu DAO. Quem vai gerar/criar essa connection/session não deve me interessar muito.

Sei lá, ter que extender HibernteDAOSupport vai gerar um acoplamento forte ao Spring que pode complicar depois na hora de testar. Deve ter uma outra maneira via Spring de fazer isso via DI puro, sem acoplamentos.

Mauricio_Linhares

E como eu já havia dito, tem, basta usar um FactoryBean de sessoes (não lembro no nome da classe agora…).

O problema é que o HibernateDaoSupport facilita um bocaaaaaaaado de coisas, então na maioria das vezes se prefere utilizar ele :smiley:

saoj

Sei lá. Eu vejo DAO como algo altamente independemente e desacoplado de qualquer outra coisa que não seja uma connection ao banco ou uma session do hibernate. Mais ou menos como o meu modelo de negócios.

Existe tb um ConnectionDaoSupport ?

Se vc esquecer de fechar a session, como a implementação acima está fazendo, o HibernateDaoSupport fecha pra vc ?

M

saoj:
Sei lá. Eu vejo DAO como algo altamente independemente e desacoplado de qualquer outra coisa que não seja uma connection ao banco ou uma session do hibernate. Mais ou menos como o meu modelo de negócios.

Existe tb um ConnectionDaoSupport ?

Se vc esquecer de fechar a session, como a implementação acima está fazendo, o HibernateDaoSupport fecha pra vc ?


Eu tbm concordo contigo, no meu entendimento o DAO nao deve extender a esta classe não, e contrário do q o Mauricio disse, tem como sim injetar via DI e continuar tendo todos benefícios do Spring, e quanto ao controle do Session, o Spring faz tudo isto sozinho sim, abre, fecha, etc, etc, etc.

M

Maurício Linhares:
saoj:
Todos os seus Daos vão ter que extender HibernateDaoSupport ? Não é melhor fazer isso com DI ?

Se você quizer todas as facilidades da integração do Spring com o Hibernate, tem que ser sim, o que não é nenhum problema, já que os módulos do Spring são independentes :smiley:

Nâo tem não, eu uso DI aqui, e continuo tendo todos os benefícios.

Kenobi

saoj:
Sei lá. Eu vejo DAO como algo altamente independemente e desacoplado de qualquer outra coisa que não seja uma connection ao banco ou uma session do hibernate. Mais ou menos como o meu modelo de negócios.

Existe tb um ConnectionDaoSupport ?

Se vc esquecer de fechar a session, como a implementação acima está fazendo, o HibernateDaoSupport fecha pra vc ?

Sim ele fecha / gerencia tudo pra vc . Quem já usou sabe da enorme facilidade e produtividade que o mesmo dá ao sistema.

Tem horas que precisamos olhar para o quanto estamos sendo produtivos, fazendo tudo no braço, para tornar a arquitetura mais elegante.

A solução que utilizo é gerar interface para os Daos e fazer DI na camada de negócios do mesmo.

Hoje estou usando a implementação Hibernate do Spring, se amanhã eu mudar, reescrevo e mudo somente o xml do DI .

Simples, prático e rápido :slight_smile: E o melhor, desacoplado da camada de business … :slight_smile:

saoj

Eu utilizo um filtro/interceptor que me entrega uma session do Hibernate e depois se encarrega de fechá-la, logo após a action ser executada, mesmo se ela jogar uma exception.

Isso deves ser responsabilidade do framework não sua. Claro que um pouco sua também.

Eu faço DI via filtro/interceptor. Sua camada de negócios em tese não deveria saber o que é uma session ou connection. O seu modelo de negócios deveria usar apenas o seu DAO. O Dao é que precisa se preocupar com isso.

Exatamente. Se amanhã mudar ao invés de injegar HibernateUserDao, vc injeta OracleUserDao ou FileUserDao.

:wink:

bmentges

Saoj,

Sem querer ser chato cara, mas ja sendo :stuck_out_tongue:

Sempre que alguém fala sobre o Spring MVC voce entra com tudo na discussao criticando as arquiteturas dos outros, que utilizam o dito cujo…

Pega leve :slight_smile:

Abraços,
Bruno Carvalho

bmentges

Na minha arquitetura de um projetinho web que fiz ta assim:

DatabaseFacade (Interface)
|
- DatabaseFacadeImpl (injected) [color=white]....[/color]| [color=white]....[/color]- UsuarioDao (Interface)
[color=white]…[/color]|[color=white]…[/color]|
[color=white]…[/color]|[color=white]…[/color]- UsuarioDaoImpl (injected) [color=white]....[/color]- EmpresaDao (Interface)
[color=white]…[/color]etc…

Sendo assim, a minha implementacao de UsuarioDaoImpl extende HibernateDaoSupport e implementa UsuarioDao.

Se eu quiser testar, basta mudar no XML quem implementa UsuarioDao e pronto. Tenho um mock lindo ;).

abraços,
Bruno Carvalho

saoj

bmentges:
Saoj,

Sem querer ser chato cara, mas ja sendo :stuck_out_tongue:

Sempre que alguém fala sobre o Spring MVC voce entra com tudo na discussao criticando as arquiteturas dos outros, que utilizam o dito cujo…

Pega leve :slight_smile:

Abraços,
Bruno Carvalho

Desculpa, mas vou ter que discordar inteiramente com vc aqui. Acho que está claro que estamos discutindo boas práticas de arquitetura, e que isso independe de framework. Não estou criticando nada, estou apenas debatendo.

Eu estou tentando pegar leve. Vc poderia fazer o mesmo tb, não acha ? :roll:

M

bmentges:
Saoj,

Sem querer ser chato cara, mas ja sendo :stuck_out_tongue:

Sempre que alguém fala sobre o Spring MVC voce entra com tudo na discussao criticando as arquiteturas dos outros, que utilizam o dito cujo…

Pega leve :slight_smile:

Abraços,
Bruno Carvalho


Acompanho bastante o forum (lendo), e nunca quis comentar isto, mas já q tú comentou, não poderia de me expressar tbm, tudo que passa de outros frameworks o saoj comenta algo para diminuir o falor deste, isto realmente esta ficando cansativo.

saoj

mambira:

Acompanho bastante o forum (lendo), e nunca quis comentar isto, mas já q tú comentou, não poderia de me expressar tbm, tudo que passa de outros frameworks o saoj comenta algo para diminuir o falor deste, isto realmente esta ficando cansativo.

Acho que o espaço aqui é democrático e livre. Vc poderia me mostrar onde eu critiquei alguma coisa ?

A discussão era se devemos extender um DAO base ou usar DI. Falei mal de alguem ou de algum framework ? Vc até concordou comigo! :roll:

kuchma

Pessoal, esta thread ate que esta bem-comportada, vamos acalmar os animos. Desta vez estou com o saoj.

Marcio Kuchma

M

saoj:
Acho que o espaço aqui é democrático e livre. Vc poderia me mostrar onde eu critiquei alguma coisa ?

A discussão era se devemos extender um DAO base ou usar DI. Falei mal de alguem ou de algum framework ? Vc até concordou comigo! :roll:


Estou falando por outras threads, mas concordo que estou errado e peço desculpas, oq passou, passou, e já até perdeu, nen existe +. Bola pra frente.

diogoj
fviana:
bom, a ideia de DAO é fazer o acesso aos dados, ou seja, execução dos sql's...

O ideal seria vc criar um BusinessDelegate e um BusinessObject.
O BusinessObject contendo as regras de negocio e acessando o DAO.
O BusinessDelegate sendo a ligação entre o Controller e o BusinessObject.

Ex:
class ClienteBO{
  ClienteDao dao;
  public viod inserir(Cliente cliente){
    if (cliente.getDataCadastro() == null) // uma RN
      cliente.setDataCadastro(new Date());
    dao.inserir(cliente);
  }
}

class ClienteBD{
  ClienteBO bo;
  public viod inserir(Cliente cliente){
    bo.inserir(cliente);
  }
}

class ClienteController extends Controller{
  ClienteBD bd;
  public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {                
    Cliente cliente = new Cliente();
    bd.inserir(cliente);
    return new ModelAndView("confirmacao");
  }
}

Então uma possível "solução" para retirar o Dao do Controller seria o uso de um business delegate? fviana, recomendam alguma leitura?

Mauricio_Linhares

BusinessDelegate tem cheiro de session bean que tem cheiro de programação procedural :stuck_out_tongue:

Z

Se a aplicação usa Hibernate e Spring (isso é invariante, não é questionado o uso deles), não é problema nenhum que a implementação de um DAO tenha uma alta dependência de algumas classes desses frameworks. Principalmente quando essa classe ajuda bastante o trabalho a ser feito.

Pra mim, é um tanto lógico que o meu HibernateClienteDAO, em uma aplicação gerenciada pelo Spring, use tranquilamente a HibernateSupportDAO…

diogoj

Como vc encara essa “questao” Mauricio? Não ve problema em ter o Controller referenciando diretamente o DAO? Qual seria uma possível solução na sua opinião?

Thiago_Senna

Dependendo da aplicação, qual é o problema de acessar uma interface do DAO na camada de controle?

Se a aplicação for simples e tiver apenas um cliente, como a maioria das aplicações web por ai, não vejo motivo para esconder a interface do DAO da camada de controle.

Agora se fazem tanta questão, não precisa de um Business Delegate. Para falar a verdade, nunca tive interesse em saber o que é isso, apesar do nome ser muito bonito.

É só fazer com que seu controle chame um façade que conhece seus daos e suas classes de domínio.

view --> controle --> façade --> dominio (BO) + DAO

Mauricio_Linhares

Como vc encara essa “questao” Mauricio? Não ve problema em ter o Controller referenciando diretamente o DAO? Qual seria uma possível solução na sua opinião?

Nops, não vejo problema nenhum, você vê algum?

Se você tem a lógica da sua aplicação dentro das classes do seu modelo, o controller vai pro lixo mesmo, não faz diferença se você mete um DAO lá ou não, ele é inútil se você trocar de framework ou sair de desktop pra web, já as suas classes de negócio continuam lá pra serem reutilizadas em qualquer outro projeto.

Não consigo entender porque o DAO no controller é um problema.

Mauricio_Linhares

BusinessDelegate é um nome pomposo pra façade. Porque mesmo agente quer um façade aqui?

diogoj

Também não vejo, mas alguns artigos que li ( nao tenho o link para eles agora ) dizem q nao é uma boa prática o Controller ter acesso direto ao DAO …

Concordo :thumbup:

Maurício Linhares:

Não consigo entender porque o DAO no controller é um problema.

Também não consigo “enxergar” onde pode estar o problema … vai ver ele não existe …

Vou ver se consigo encontrar os artigos que me referi anteriormente.

Vlw :smiley:

Kenobi

Maurício Linhares:
Thiago Senna:

É só fazer com que seu controle chame um façade que conhece seus daos e suas classes de domínio.

view --> controle --> façade --> dominio (BO) + DAO

BusinessDelegate é um nome pomposo pra façade. Porque mesmo agente quer um façade aqui?

A verdade é que depois de 2, 3 cervejas, todos esses partterns ficam sendo a mesma coisa :slight_smile:

Thiago_Senna

Há, então é isso? Pensei que era algo pomposo também! :smiley:

Bom, assim como você não vejo problemas de acessar o DAO no controle, eliminando-se assim este façade.

Só vejo utilidade neste façade se for para criar uma espécie de camada de serviço. Assim, um método que conhece o DAO + domínio seria reaproveitado por clientes diferentes ao invés de repetir esta lógica em dois controles diferentes.

No entanto, se sua aplicação tem apenas um cliente, então não vejo por que usar este façade.

É isso mesmo?

Mauricio_Linhares

Se você der uma olhada no business delegate da Sun vai ver que a idéia é mais ou menos essa, só que lá eles vem com toda aquela história de “invocação remota, lookup de EJBs, conexões com bancos de dados” e de como tudo isso deve ficar abstraído pra o cliente do código que é exatamente a idéia do Façade, simplificar o acesso a um conjunto de objetos que estão trabalhando em conjunto (o exemplo de controle remoto universal do Head First Design Patterns ficou maravilhoso :D).

Mas no nosso caso específico, eu acredito que não existe necessidade disso, nem existe a tal da duplicação do código. É melhor começar simples e depois, se realmente houver duplicação, faz um refactoring, é até melhor ir dando um passo de cada vez :stuck_out_tongue:

Thiago_Senna

Concordo! Até porque este refactoring não é tão complicado! :smiley:

Que bom, pois meu Head First Desgin Patterns chegou em casa segunda-feira! Uauauauau

Criado 27 de março de 2006
Ultima resposta 5 de abr. de 2006
Respostas 34
Participantes 11