"Arquitetura" para camada de negócios com Spring e Hibernate

Olá pessoal,

Depois de algumas pesquisas, montei uma “arquitetura” para a camada de negócios usando Spring e Hibernate.

Até ai tudo bem, achei que a divisão de responsabilidades entre as classes e camadas estava bom, só que após alguns meses de programação, a aplicação foi crescendo e estou me questionando, sobre a eficácia dessa estrutura.

  1. Tenho as classes do modelo (Value Object), exemplo:
  • Usuario
  • Endereco
  • Tipo
  1. Crio os DAOs com base em um DAO genérico, como este:
    http://blog.caelum.com.br/2006/08/26/ei-como-e-o-seu-dao-ele-e-tao-abstraido-quanto-o-meu/

Exemplos:

  • UsuarioDAO
  • EnderecoDAO
  • TipoDAO
  1. Regras de negócio
    Ex: Quando eu salvo um usuário, eu tenho que salvar o seu endereço também, logo eu criei uma classe chamada UsuarioService, e no método salvar há uma transação gerenciada pelo Spring, exemplo:
public void salvar(Usuario u) {
   getEnderecoDao().saveOrUpdate(u.getEndereco);
   getUsuarioDao().saveOrUpdate(u);
}

Notem que a classe UsuarioService possui instancias preenchidas pelo Spring de UsuarioDAO e EnderecoDAO.

Tenho classes como essas que precisam as vezes acessar 10 DAOs diferentes. Isso não me parece uma boa prática, o que vocês acham?

Como vcs organizam estas responsabilidades?

A mesma pergunta que o Luca fez ontem para uma pessoa aqui no GUJ eu faço para você: O que é VO para você?.

EndereçoDAO é desnecessário, seguindo a definição de Value Object de DDD, endereço é um VO (no JPA, um objeto embeddable). Não é preciso ter DAO para ele, ele estará dentro de outros objetos (no caso, acho que usuário).
Se você tivesse feito assim, não seria necessário criar aquele service, já que apenas um save no usuário resolveria o problema.

Pelo que entendi, não está sendo usado DDD nesse modelo de arquitetura, até mesmo por causa do uso de services e da ausencia de um repositório explicito. De qualquer forma, acho que os DAOs devem ser chamados de acordo com a necessidade e não apenas porque voce precisa gravar uma classe. O que pode acontercer é que existe memso um erro na modelagem das suas classes.
Como disse o pafuncio, experimente alterar a classe Endereço para se tornar um componente de Usuario. Assim o proprio hibernate já realiza a persistência dele no local correto sem voce precisar chamar mais um DAO. Isso pode ser útil também para persistir coleções de objetos que foram mapeadas como relacionamentos *ToMany.

Olá,

Pafuncio, para mim Value Object são objetos POJO do modelo de negócio da aplicação.

Legal a idéia de usar o Endereco como um embeddable. Mas, uma vez feito isso, como vcs organizariam essa estrutura?

Por exemplo, na camada de apresentação, eu sempre colocaria uma classe de serviço entre as camadas, ou permitiria que o camada de apresentação tenha acesso direto aos DAOs?

[quote=ccalixto]Olá,

Pafuncio, para mim Value Object são objetos POJO do modelo de negócio da aplicação.

[/quote]

CCalixto, o conceito de Value Object segundo Domain Driven Design é que são objetos que não possuem uma identidade específica mas que dão algum significado à uma entidade (essa sim possui identidade). Ou seja, no seu caso, Usuario é uma entidade e Endereco é um Value Object que estará contido nessa entidade, seja por uma agregação ou composição.

Mas pelo que eu entendi você usa suas classes de domínio apenas para mapear os dados necessários, como um mero saco de get/set. Se eu estiver correto, desconsidere esse conceito, isso é um exemplo clássico de um domínio anêmico (http://www.guj.com.br/posts/list/55388.java, uma discussão antiga aqui sobre Anemic Domain Model) . As classes de domínio devem implementar também as regras de negócio relacionadas com a própria. Ou seja, sua classe Usuario, provavelmente terá a seguinte cara:

public class Usuario {
   public Usuario() {}

   private Integer id;
   private String nome;
   private String login;
   private String senha;
   private Endereco endereco;

  //métodos get/set

  public boolean checarAlteracaoSenha(String senhaAntiga, String senhaNova, String confirmacaoSenhaNova) {

     ... //Aqui dentro vc verifica se a senha nova é diferente da senha antiga, se a confirmação da senha é igual a senha nova... etc...
  }
   
  public boolean validarLogin(String loginInformado, String senhaInformada) {
     ...//Aqui vc faz as verificações necessárias
  }

  //etc
}

Um service deve ser usado no caso de você ter uma operação para executar mas que ela não se encaixe semânticamente em uma classe de domínio específica, por exemplo, envio de e-mails. Você encapsularia toda a sua complexidade de envio de e-mails num EmailService e à partir daí delegaria os envios para esse service. Então, por exemplo, imagine que vc queira mandar e-mail toda vez que o usuário errar o login (isso é um cenário mto irreal, mas vamos com ele mesmo), no seu método validarLogin() dentro da classe Usuario vc faria uma chamada para EmailService caso o login fosse errado.

Com relação a camada de apresentação acessar direto os DAOs, não vejo problema nisso, por exemplo, no seu ManagedBean (no caso de JSF):

public String efetuarLogin() {
   Usuario usuario = usuarioDAO.getUsuarioByLogin(loginInformado);
   if(usuario != null) {
      if(usuario.validarLogin(loginInformado, senhaInformada))
         return "logado";
      else 
          return "senhaInvalida";
   } else {
      return "usuarioinexistente";
   }
}

PS: Fica mais elegante se você encapsular os DAOs dentro de repositories, ou então tirar os DAOs e deixar apenas repositories, aí vai de você para ver se é interessante manter esses DAOs ou não, mas é bom tirar esses usuarioDAO.getUsuarioByLogin que não dizem mta coisa para o domínio. Dê uma lida sobre o assunto. :wink: