Impasse entre lentidão e quebra de camadas

Pessoal, bom dia !

Considerem o exemplo abaixo:

No método “getDataUltimaVenda” o correto seria fazer um “FOR” e ficar procurando qual seria a última data de venda e retorná-la. Porém tenho o receio que isso com o tempo possa ficar lento, porque o número de pedidos irá aumentando com o tempo.
Uma solução seria fazer uma busca no banco de dados, porém não devo fazer acesso ao banco a partir das minhas classes de domínio.

Gostaria de sugestões de como resolver isso ?

Exemplo das classes:

[code]public class Pedido {

//outros atritutos

Date dataVenda;

@ManyToMany
List<Produto> produtos;

//restante da classe;

}

public class Produto {

//outros atritutos

@ManyToMany	
List<Pedido> pedidos;

Date getDataUltimaVenda() {
	// ??????????
}

//restante da classe;

}
[/code]

cara,

vc pode fazer um select max data pedido, vc pode ter no seu DAO uma sql normal, que nao necessite usar hql ou criteria.

T+

[quote=alissonvla]cara,

vc pode fazer um select max data pedido, vc pode ter no seu DAO uma sql normal, que nao necessite usar hql ou criteria.

T+[/quote]
Vlw pela resposta.
Mas então cara, tanto faz ser hql, criteria ou não. Isso não mudaria em nada.
A questão é, como fazer isso no “get” que esta dentro do produto sem ferir a camada e ficar fazendo “FOR” ? Pois teoricamente essa seria forma correta.

entao,

eu no meu caso faria esse seu caso passando pela camada de negocio e depois camada de persistencia, não colocaria isso direto na sua entidade.

t+

uma dica, não sei se vale pra vc

eu faço assim

Select minhadata From tabela Order By minhadata desc Limit 1;

[quote=alissonvla]entao,

eu no meu caso faria esse seu caso passando pela camada de negocio e depois camada de persistencia, não colocaria isso direto na sua entidade.

t+[/quote]

nei.junior e alissonvla

Essa sugestão seria uma solução, porém ao meu ver não fica tão OO quanto se estivesse dentro da entidade, a não ser que suas entidades sejam apenas objetos de valor (o que daria outra discussão enorme). Além disso, se colocar isso na camada de negócio ou persistência eu ainda vejo uma dificuldade caso você tenha que exibir uma lista (uma table por exemplo) com os atributos do seu produto e se esse valor ‘dataUltimaCompra’ tiver que ser listado, terá um certo trabalho para poder exibir isso.

[quote=danilo.magrini]
nei.junior e alissonvla

Essa sugestão seria uma solução, porém ao meu ver não fica tão OO quanto se estivesse dentro da entidade, a não ser que suas entidades sejam apenas objetos de valor (o que daria outra discussão enorme). Além disso, se colocar isso na camada de negócio ou persistência eu ainda vejo uma dificuldade caso você tenha que exibir uma lista (uma table por exemplo) com os atributos do seu produto e se esse valor ‘dataUltimaCompra’ tiver que ser listado, terá um certo trabalho para poder exibir isso.[/quote]

Exatamente, concordo com o que o danilo.magrini disse.

cara,

eu penso no seu caso, como se fosse uma regra de negocio e não uma propriedade de uma entidade.
esse é meu pensamento.

t+

Começo a pensar que o método getDataUltimaVenda não deva estar na classe Produto, o que acham?

flws

Vamos lá então. Você não quer ferir o seu modelo e isso é o correto a se fazer.
Entretanto, vejo um grande problema no seu modelo, que é o Produto ter q saber quando ele foi vendido pela última vez. Ao meu ver, isso vai contra a aquele princípio da responsabilidade única (SRP).

O modelo que eu faria é o seguinte: ter um outro objeto que, dado um produto, ele saiba responder quando aquele produto foi vendido pela última vez. Mais ou menos assim

public class ConsultorProdutos{
    public Date getDataUltimaVenda(Produto produto) {
        Date ultimaVenda = null;
        //... código que busca a data da última venda

        return ultimaVenda
}

Acho que vc irá afetar seu modelo incluindo uma responsabilidade de outros objetos relacionados a view.

flws

e pra mim, tudo que envolve sql, faz parte da regra de negocio.

t+

[quote=ruivo]Vamos lá então. Você não quer ferir o seu modelo e isso é o correto a se fazer.
Entretanto, vejo um grande problema no seu modelo, que é o Produto ter q saber quando ele foi vendido pela última vez. Ao meu ver, isso vai contra a aquele princípio da responsabilidade única (SRP).
[/code][/quote]

Aqui é onde entra as enormes discussões sobre arquitetura. Ao meu ver é responsabilidade sim do Produto saber a dataUltimaVenda, pois se isso fere o princípio da responsabilidade única então teriamos que ter objetos ProdutoCodigo, ProdutoDescricao, ProdutoPreco, ProdutoUnidade, etc;

[quote=danilo.magrini]
Aqui é onde entra as enormes discussões sobre arquitetura. Ao meu ver é responsabilidade sim do Produto saber a dataUltimaVenda, pois se isso fere o princípio da responsabilidade única então teriamos que ter objetos ProdutoCodigo, ProdutoDescricao, ProdutoPreco, ProdutoUnidade, etc;[/quote]

De maneira alguma. O que é uma venda? Nada mais é que um pedido finalizado. E é um pedido que tem um produto, não o contrário. Pensa numa garrafa. Ela não tem gravada nela quando foi vendida. Agora, se pensar na nota fiscal, lá estarão as referências para os produtos que foram vendidos, com as datas e valores pagos pelo consumidor.

Se você quiser saber qual a data que um produto foi vendido pela última vez, você deve perguntar para os pedidos (ou notas fiscais) qual a última vez que aquele produto aparece, e não perguntar pro produto quando ele foi vendido, que não faz muito sentido.

Isso é um dilema de arquitetura mesmo. Na minha opiniao, a data da ultima venda é uma regra de negocio, como tambem data da ultima compra, media de preco vendida, meses onde ocorre maior venda, etc. Para usar estas informacoes calculadas em uma table ou um relatorio, o melhor seria fazer estes calculos e possiveis acessos ao BD em uma classe DAO, que retornasse, por ex., uma lista de produtos com data de ultima venda. Eu inclusive crio uma classe auxiliar e faco no DAO um “SELECT new classeAuxiliarXXX(lista de campos) FROM… WHERE…”, e nas tables/relatorios uso a classe auxiliar, pois fica facil acessar todos os campos, que podem vir de Produto, Venda, Cliente, etc.

Concordo com o ruivo, provavelmente a sua regra está no lugar errado - mas isso é apenas a visão minimalista que tenho do seu modelo de negócios.

No mais, talvez seja melhor colocar no repositório de Vendas ou Pedidos, novamente depende.

Se ainda assim você tiver necessidade e interesse que a regra continue na sua entidade produto, aconselho você tentar umas das alternativas abaixo:

  1. Use a anotação @Formula - http://stackoverflow.com/questions/2986318/calculated-property-with-jpa-hibernate ;
  2. Passe um repositório para o método getter, algo como produto.getDataDaUltimaVenda(repositorio) ;

Um abraço.

[quote=ruivo][quote=danilo.magrini]
Aqui é onde entra as enormes discussões sobre arquitetura. Ao meu ver é responsabilidade sim do Produto saber a dataUltimaVenda, pois se isso fere o princípio da responsabilidade única então teriamos que ter objetos ProdutoCodigo, ProdutoDescricao, ProdutoPreco, ProdutoUnidade, etc;[/quote]

De maneira alguma. O que é uma venda? Nada mais é que um pedido finalizado. E é um pedido que tem um produto, não o contrário. Pensa numa garrafa. Ela não tem gravada nela quando foi vendida. Agora, se pensar na nota fiscal, lá estarão as referências para os produtos que foram vendidos, com as datas e valores pagos pelo consumidor.

Se você quiser saber qual a data que um produto foi vendido pela última vez, você deve perguntar para os pedidos (ou notas fiscais) qual a última vez que aquele produto aparece, e não perguntar pro produto quando ele foi vendido, que não faz muito sentido.[/quote]

Acho que essa solução é um bom “meio termo”.
Sem ser totalmente purista, mas também sem perder performance.

Uma coisa a se pensar é se essa data aparece em muitos lugares ou é só em algum relatório específico.

[quote=rponte]Concordo com o ruivo, provavelmente a sua regra está no lugar errado - mas isso é apenas a visão minimalista que tenho do seu modelo de negócios.

No mais, talvez seja melhor colocar no repositório de Vendas ou Pedidos, novamente depende.

Se ainda assim você tiver necessidade e interesse que a regra continue na sua entidade produto, aconselho você tentar umas das alternativas abaixo:

  1. Use a anotação @Formula - http://stackoverflow.com/questions/2986318/calculated-property-with-jpa-hibernate ;
  2. Passe um repositório para o método getter, algo como produto.getDataDaUltimaVenda(repositorio) ;
    [/quote]

O caso 1) é na minha opinião a melhor opção mas o provedor de persistência deveria ser o Hibernate
O caso 2) é uma boa opção pois mesmo que não utilize repositórios poderia implementar um somente para esse caso, porém o nei.junior deverá avaliar se isso é viável pra ele ou não.

[quote=rponte]Concordo com o ruivo, provavelmente a sua regra está no lugar errado - mas isso é apenas a visão minimalista que tenho do seu modelo de negócios.

No mais, talvez seja melhor colocar no repositório de Vendas ou Pedidos, novamente depende.

Se ainda assim você tiver necessidade e interesse que a regra continue na sua entidade produto, aconselho você tentar umas das alternativas abaixo:

  1. Use a anotação @Formula - http://stackoverflow.com/questions/2986318/calculated-property-with-jpa-hibernate ;
  2. Passe um repositório para o método getter, algo como produto.getDataDaUltimaVenda(repositorio) ;

Um abraço.
[/quote]

É, no caso de usar o @Formula, seria uma boa. Porém aqui utilizo EclipseLink e já procurei algo parecido para fazer e não achei.

Mas quero agradecer todos que deram sugestões. Com base em tudo isso, vou analisar e ver qual a melhor forma de implementar aqui.
Pelo que vi, não tem certo ou errado, e sim a forma que melhor se encaixar no projeto.

Quem tiver mais ideias ou sugestões, compartilhe se possível !

Obrigado a todos.

Eu prefiro uma entidade rica que tem o get que informa isso fazendo consulta no banco mas o padrão que costumam usar no Java é entidade burra então faz um método no DAO que calcula isso

Uma opção extra seria você desnormalizar o banco armazenando a data da última venda em uma coluna de produto e gerenciando essa coluna na aplicação ou via trigger. De quebra vc ainda resolve seu problema de poder ter um get no Produto informando isso

As vezes é necessário desnormalizar por questões de performance, não há nada de errado nisso. Ninguém por exemplo escreve um software de fórum e faz count(*) pra exibir o número de tópicos e posts do fórum, isso é armazenado em uma coluna na entidade Forum

Com o modelo desnormalizado vc não teria que fazer 20 queries adicionais caso listasse os produtos junto com a data de ultima venda. Ou então se tivesse um índice nessa coluna um relatório de produtos que tivessem data X como última venda seria instantâneo. Analise suas necessidades