Arquitetura de uma aplicação WEB + MVC + Frameworks action-based

Olá pessoal relendo alguns posts do GUJ, como este http://www.guj.com.br/posts/list/15/60916.java
e outros do blog da caelum como este http://blog.caelum.com.br/2007/06/09/repository-seu-modelo-mais-orientado-a-objeto entre outros lugares, comecei a me questionar sobre a maneira como tenho arquiteturado minhas aplicações WEB.

Atualmente quando vou fazer um CRUD, faço da seguinte maneira, tentando ser o mais simples possível, utilizando algum framework MVC.

REQHTTP - > Action -> DAO -> SGDB

OBS
*Quando os dados chegam na Action, o POJO já vem populado pelo meu framework, como mentawai ou VRaptor.

*Minha classe Action não tem um Dao como atributo, o mesmo é instanciado dentro de cada método, isso porque utilizo um Dao generico(antes utilizava um dao para cada classe e injetava o mesmo por IoC), mas hoje prefiro a abordagem de um Dao generico, uma vez que consegui diminuir muito os numeros de .java na minha aplicação.

Quando preciso de uma funcionalidade mais “complexa”, digamos logo apos o cadastro de um usuario mandar um e-mal para o mesmo. Neste caso crio um service, como no exmplo;

[code]
class UserService {

public void save(Usuario usuario) //Usuario aqui vem populado, enviado pela action{
HibernateDao usuarioDao=new HibernateDAO<Usuario.class>();
usuarioDao.save(usuario);
sendMail();

}

private sendMail(){
//codigo para enviarEmail
}

}

As lógicos de negócio ficam nas classes de negócio como, a action servindo somente para recuperar os parametros da view e mandar para o model.

public class UserAction{

public changePassword(){
String oldPassword=input.getStringValue("oldPassword");
String newPassword=input.getStringValue("newPassword");


Usuario user=(Usuario)session.getAtributte("user");

user.changePassord(oldpassword,newpassword);

//try -catch -aqui tratando as excecoes da camada de negocio;

}

}

public class User

public changePassword(String olPassword, String newPassword){

if(senha.equals(oldPassword){
  setSenha(newPassword);
}else{
 throw new BussinessException("Senha invalida"); //Exceção qualquer de negocio
}

}

Uma nova abordagem que estou pensando a utilizar é a seguinte:
REQHTTP -> Action -> Modelo de Negocio > Repository(interface) > Dao> SGB

Como neste artigo http://blog.caelum.com.br/2007/06/09/repository-seu-modelo-mais-orientado-a-objeto onde o autor fala ao inves de ter um codigo como este:

public class Logica { // extends Action? ;)
  public void mostraFornecedorEContasPagas() {
    Long id = request.getParameter("id"); 
    Fornecedor f = fornecedorDAO.load(id)
    List<Conta> contasPagas = contasDAO.listaContasPagasDoMes(fornecedor);
    // ejeta tudo para mostrar na view. No século passado seria:
    request.setAttribute("fornecedor", fornecedor);
    request.setAttribute("contasPagas", contasPagas);
  }
}

Pode ser substituido por
<c:forEach items="${fornecedor.ContasPagasDoMes}" var=“contas”>
<c:out value="${conta.qualquerCoisa}"/>
</c:forEach>

Onde Fornecedor tem um repository de contas!

Realmente fica bem mais OO, mais vejo algumas desvantagens, isso não seria uma violação de camada ou seja a view, está acessando diretamente o DAO, pode argumentar que repository é um conceito de negocio, tudo bem, mas no caso seria so um delegate para o DAO, isso não seria violar camadas?

Estou pensando em usar a segunda abordagem, mas antes queria ouvir sugestões. Qual estrutura geralmente vcs usam para acessar um SGDB:

REQHTTP -> Action ->Dao > SGB
REQHTTP -> Action -> Modelo de Negocio > Repository(interface) > Dao> SGB

ou ainda

req HTTP -> Action -> Modelo -> Repository -> Implementação concreta de Repository -> DAO -> Implementação concreta de UserDAO -> Banco de dados

Valeu

Eu mesmo já embazei diversas vezes o WEB -> Action -> Service/Logic -> DAO/Repository -> DB

Mas muitas vezes isso é um overkill, um tédio de redundancia e classes desnecessárias.

Dependendo do projeto eu hoje faço WEB -> Action -> DAO -> DB. Teoricamente isso não é o mais certo, mas na prática e disparado o mais conveniente. Muitos projetos RoR são feitos assim.

Se vc está preocupado com acoplamento, dê uma olhada nesse artigo:

http://www.theserverside.com/news/thread.tss?thread_id=48304

[quote=saoj]Eu mesmo já embazei diversas vezes o WEB -> Action -> Service/Logic -> DAO/Repository -> DB

Mas muitas vezes isso é um overkill, um tédio de redundancia e classes desnecessárias.

Dependendo do projeto eu hoje faço WEB -> Action -> DAO -> DB. Teoricamente isso não é o mais certo, mas na prática e disparado o mais conveniente. Muitos projetos RoR são feitos assim.

Se vc está preocupado com acoplamento, dê uma olhada nesse artigo:

http://www.theserverside.com/news/thread.tss?thread_id=48304[/quote]

Eu também acho que as vezes podemos chamar o DAO\Repository direto da Action (telas de pesquisa, por exemplo), mas isso acaba sendo um problema sério para manutenção.

Mesmo que você diga que se um dia precisar de mais coisas você vai refatorar e colocar num objeto de negócio, muitas vezes isso não é feito, é muito mais fácil fazer certo da primeira vez do que deixar pra depois “quando precisar”.

Geralmente mesmo para CRUDs, as operações de “update” eu prefiro colocar em classes de negócio. Sempre tem uma verificação, uma validação, um fluxo, um workflow que deve ser executado antes ou depois de criar ou alterar uma entidade e fazer dao.save na action é medir pra vir um code monkey depois e colocar código na action que não deveria estar na action… Já deixar tudo separado corretamente orienta melhor quem for dar manutenção no código depois.

Depende do estilo que você quer fazer.

Se a lógica de sua aplicação estão em classes comumente chamadas “de negócio” (não gosto desse termo, prefiro classes de serviço). Então é natural pensar que toda Action sempre invoca um método da classe de Serviço.

Agora, ao colocarmos as lógicas nas próprias entidades (e, segundo alguns, deixando mais OO), então as classes de serviço desaparecem e não é nada de errado a Action obter uma entidade de um repositório e depois invocar um método desse objeto retornado.

É importante frisar que DAO não é repositório. DAO possui uma abstração puramente de persistência e repositório é uma abstração de negócio. Pode-se implementar o Repositório como uma interface da camada de negócio e o DAO como uma classe que a implementa da camada de persistência. Mas não é o único jeito e não precisa ser assim por toda a sua aplicação.

Como relação ao fato de, se a view chamar algo do objeto do domínio, então ocorreria alguma violação, a resposta é não. Pelo MVC é permito a view acessar o model. Pela arquitetura em camadas, a camada de apresentação (sua action e seu jsp) pode ter acesso à camada de negócio (suas entidades, repositórios, eventuais serviços e factories).

[quote=Leonardo3001]
É importante frisar que DAO não é repositório. DAO possui uma abstração puramente de persistência e repositório é uma abstração de negócio. Pode-se implementar o Repositório como uma interface da camada de negócio e o DAO como uma classe que a implementa da camada de persistência. Mas não é o único jeito e não precisa ser assim por toda a sua aplicação.[/quote]
Por favor, não vamos voltar nessa ladainha de que DAO não é repositório. Já foi inumeras vezes discutidas aqui e ficou claro que o ponto não é esse.

[quote=Rubem Azenha][quote=saoj]Eu mesmo já embazei diversas vezes o WEB -> Action -> Service/Logic -> DAO/Repository -> DB

Mas muitas vezes isso é um overkill, um tédio de redundancia e classes desnecessárias.

Dependendo do projeto eu hoje faço WEB -> Action -> DAO -> DB. Teoricamente isso não é o mais certo, mas na prática e disparado o mais conveniente. Muitos projetos RoR são feitos assim.

Se vc está preocupado com acoplamento, dê uma olhada nesse artigo:

http://www.theserverside.com/news/thread.tss?thread_id=48304[/quote]

Eu também acho que as vezes podemos chamar o DAO\Repository direto da Action (telas de pesquisa, por exemplo), mas isso acaba sendo um problema sério para manutenção.

Mesmo que você diga que se um dia precisar de mais coisas você vai refatorar e colocar num objeto de negócio, muitas vezes isso não é feito, é muito mais fácil fazer certo da primeira vez do que deixar pra depois “quando precisar”.

Geralmente mesmo para CRUDs, as operações de “update” eu prefiro colocar em classes de negócio. Sempre tem uma verificação, uma validação, um fluxo, um workflow que deve ser executado antes ou depois de criar ou alterar uma entidade e fazer dao.save na action é medir pra vir um code monkey depois e colocar código na action que não deveria estar na action… Já deixar tudo separado corretamente orienta melhor quem for dar manutenção no código depois.
[/quote]

Rubem, só para ficar claro para mim, quando você disse “classes de negócio” quer dizer tipo UsuarioService, ou quis dizer a propria entidade Usuario?

Hum… digamos que tanto faz. Se a regra de negócio precisa ser “orquestrada”, UsuarioService, caso contrario, vai na própria entidade Usuario mesmo.

[quote=Rubem Azenha][quote=danielbussade]
Rubem, só para ficar claro para mim, quando você disse “classes de negócio” quer dizer tipo UsuarioService, ou quis dizer a propria entidade Usuario?
[/quote]
Hum… digamos que tanto faz. Se a regra de negócio precisa ser “orquestrada”, UsuarioService, caso contrario, vai na própria entidade Usuario mesmo.[/quote]

Supondo então que a regra de negócio não precise ser orquestrada. Ai você coloca as operacoes de CRUD todas na classes do seu Domain?

E no domain você acesso o que, uma interface Dao? Ou coloca a implementação Dao direto?

[quote=danielbussade]Supondo então que a regra de negócio não precise ser orquestrada. Ai você coloca as operacoes de CRUD todas na classes do seu Domain?

E no domain você acesso o que, uma interface Dao? Ou coloca a implementação Dao direto?[/quote]

Talvez eu leve uma segunda “patada” por desviar o tópico, mas enfim… vamos lá.

Se você tem apenas CRUD, então toda a arquitetura é desnecessária, e basta fazer uma tela refletir as operações do banco. Mas a realidade não é essa, CRUD é superenfatizado, e na realidade, operações estritamente CRUDs são mais raras do que muita gente imagina. Afinal, não é tudo que pode excluído, atualizações podem exigir regras de carência, inserções são seguidas de validações e só podem ser feitas por um seleto grupos de pessoas, e etc.

Mas entendo a sua dúvida, você quer saber se a própria entidade deve lidar com sua própria persistência. Pois bem, certas coisas são impossíveis - exemplo: o SELECT - porque um objeto não pode consultar a si mesmo, se este ainda não existe. É necessário um outro objeto pra isso, o Repositório. As outras operações até podem ser feitas pelo próprio objeto, mas como é difícil injetar infraestrutura em objetos de domínio (você dependeria de factories), é melhor ter o repositório fazer as operações de escrita também.

[quote=Leonardo3001][quote=danielbussade]Supondo então que a regra de negócio não precise ser orquestrada. Ai você coloca as operacoes de CRUD todas na classes do seu Domain?

E no domain você acesso o que, uma interface Dao? Ou coloca a implementação Dao direto?[/quote]

Talvez eu leve uma segunda "patada" por desviar o tópico, mas enfim… vamos lá.

Se você tem apenas CRUD, então toda a arquitetura é desnecessária, e basta fazer uma tela refletir as operações do banco. Mas a realidade não é essa, CRUD é superenfatizado, e na realidade, operações estritamente CRUDs são mais raras do que muita gente imagina. Afinal, não é tudo que pode excluído, atualizações podem exigir regras de carência, inserções são seguidas de validações e só podem ser feitas por um seleto grupos de pessoas, e etc.

Mas entendo a sua dúvida, você quer saber se a própria entidade deve lidar com sua própria persistência. Pois bem, certas coisas são impossíveis - exemplo: o SELECT - porque um objeto não pode consultar a si mesmo, se este ainda não existe. É necessário um outro objeto pra isso, o Repositório. As outras operações até podem ser feitas pelo próprio objeto, mas como é difícil injetar infraestrutura em objetos de domínio (você dependeria de factories), é melhor ter o repositório fazer as operações de escrita também.
[/quote]

Leonardo , obrigado pelas respostas. Estava com esta idéia de colocar metodos CRUD na Entity,mas ainda acho muito esquisito as Entity, terem metodos de CRUD, acho melhor as entity refletir estritamente o Negócio, tendo somente atributos e metodos referentes ao negocio.

Da maneira como faço:
REQHttp -> Action - > DAO -> SGBD

A minha unica duvida é se ao invés de chamar um Dao direto da action, chamar um Façade como GerenciadorUsuario onde teria um metodo create(parametros_do_usuario) e neste FAçade chamaria o Dao, ou ate mesmo um Service, se precisa de alguma "orquestração", como obserevou o rubem.

Este Dao seria uma interface assim não acoplo nem minha action, nem o Façade a infraestrutura de persistencia.

Ao inves de tornar isso padrão, so crio um service ou um Façade, quando preciso de algo a "mais" assim evito delegação de camadas desnecessária.

[quote=Rubem Azenha][quote=danielbussade]
Rubem, só para ficar claro para mim, quando você disse “classes de negócio” quer dizer tipo UsuarioService, ou quis dizer a propria entidade Usuario?
[/quote]
Hum… digamos que tanto faz. Se a regra de negócio precisa ser “orquestrada”, UsuarioService, caso contrario, vai na própria entidade Usuario mesmo.[/quote]

Como assim orquestrada ?

otimo topico galera… bom sempre fiz acesso aos DAOs pela action mesmo, mas ultimamente tenho criado os service. Acho que desta forma fica até mais facil de desenvolver os testes.

Pense em “orquestrada”, como o seguinte. Digamos que tenha uma regra de Negocio em duas classes diferentes e você precisa executar as duas regras de negocio para formar uma terceira.

Ai usamos um Service, pois ele não tem estado apenas fornece um serviço , que não é responsabilidade “unica” de ninguem, mas sim responsabilidades combinadas, entre duas ou varias classes;

Qualquer duvida, pergunte!

Valeu

muito obrigado pela explicação e atençao amigo.

seguindo este ponto de vista, como vc gerencia transacoes ?

como pode usar 2 regras para formar uma terceira entao o melhor seria abrir e fechar transacoes no inicio e fim dos metodos das actions, estou certo?

abraços

Olá, na minha opinião o correto seria ter um filtro para controlar a transação, visto que qualquer uma das operações que falhasse teria que efetuar rollback na outra.

mas ai eu abriria uma transacao para cada request ? isso nao seria ruim ?

abraços

Essa é a melhor alternativa que eu vejo.

Imagine se a transação fosse menor, ou seja, um para cada método. E num request, fosse chamado dois métodos, onde cada um tivesse sua transação. Agora, pense o que aconteceria se, ao chamar o segundo método, acontecesse uma exceção qualquer. Bom, o segundo método não comitaria, mas o primeiro sim, gerando inconsistência nos dados. Se a transação fosse por request, a exceção do segundo método causaria rollback no primeiro método deixando o banco de dados válido, no mesmo estado anterior.

Agora imagine se a transação fosse maior, ou seja, um para sessão. Apesar de desejável, é pouquíssimo prático, pois no mundo real, a quantidade de conexões ao banco disponíveis é escasso. A solução é usar esquemas de lock otimista (uma coluna date, ou revision, que teria um número incremental), para tratar acessos concorrentes.

Oi leonardo, obrigado pela atençao,

é o seguinte: a transacao nao ficaria nos metodos de negocio, mas sim nas actions, logo dentro de uma action, poderia chamar quantos metodos fossem necessarios.

entendeu ?

abraços

Meu tostão neste papo.

renanreismartins,

Bacana esta saída, o problema que vejo é que em um sistema maior em termos de serviços poderia fazer com que havesse redundancia; aqueles serviços que são praticamente os mesmos mas que não daria para reaproveitar por conta da presença da transação. Outro ponto é a inclusão do código de infraestrutura em um código de serviço.

O spring (framework) oferece uma solução bastante interessante para esta questão, inclusive dizem que é melhor que a solução encontrada no EJB3.

Vcs já consideraram utilizar algum componente como este para resolver este problema?

flws

[quote=fantomas]Meu tostão neste papo.

renanreismartins,

Bacana esta saída, o problema que vejo é que em um sistema maior em termos de serviços poderia fazer com que havesse redundancia; aqueles serviços que são praticamente os mesmos mas que não daria para reaproveitar por conta da presença da transação. Outro ponto é a inclusão do código de infraestrutura em um código de serviço.

O spring (framework) oferece uma solução bastante interessante para esta questão, inclusive dizem que é melhor que a solução encontrada no EJB3.

Vcs já consideraram utilizar algum componente como este para resolver este problema?

flws

[/quote]

Concordo que existem soluções mais escaláveis, porém a arquitetura proposta é para aplicações pequenas, não me vejo utilizando ejb 3 ou spring para aplicações pequenas.

[quote=fantomas]Meu tostão neste papo.

renanreismartins,

Bacana esta saída, o problema que vejo é que em um sistema maior em termos de serviços poderia fazer com que havesse redundancia; aqueles serviços que são praticamente os mesmos mas que não daria para reaproveitar por conta da presença da transação. Outro ponto é a inclusão do código de infraestrutura em um código de serviço.

O spring (framework) oferece uma solução bastante interessante para esta questão, inclusive dizem que é melhor que a solução encontrada no EJB3.

Vcs já consideraram utilizar algum componente como este para resolver este problema?

flws

[/quote]

Fantomas, mto obrigado pela atençao amigo. Seguinte, minhas transacoes nao ficam nos servicos e sim nas actions onde posso ter inumeros serviços sendo chamados. Vc ve algum problema nessa abordagem ?

aleck, valeu cara. Que abordagem voce utiliza com relaçao as transacoes ?

abrasss a tds