IoC + DDD + Classes de negocio + Mocks

Olá pessoal, supondo que em uma classe de negocio, eu precise ter acesso a um repositorio, como eu deveria fazer isso;

Opcao 1: Declarar a classe como atributo da instancia da classe e recebe-la no construtor
ex:

public class Produto {
private ProdutoRepository produtoRepository;

public Produto(ProdutoRepository produtoRepository){

  }
}

Opcao2 : Instancia o Repositorio dentro do proprio metodo

public class Produto {
  public metodo(){
   ProdutoRepository produtoRepository=new RepositoryFactory().getProdutoRepository();
   
 }
}

Opcao3 : Passo o repositorio para o método como parametro

public class Produto {
  public metodo( ProdutoRepository produtoRepository){
   ...
   
 }

}

Desvantagens
Abordagem 1:

Como o Hibernate obriga um contrutor default, pode deixa o objeto
em um estado ilegal onde eu chamei um metodo mas seu Repositorio é nulo.

Abordagem 2:

Dificulta os testes, vistos que nao tem como Mockar, já que a depencia
esta “escondida” dentro do metodo

Abordagem 3:

A assinatura do método fica bem estranha;

Enfim, o que voces tem usado atualmente, além dessas tres que eu citei
alguém conhece uma outra forma melhor?

Obrigado

mais porque sua classe Produto precisa ter uma instancia de Repositorio?

Fernando a funcionalidade é a seguinte:

Em um sistema de e-commerce quando usuário clicar em um Produto
ele deverá ver os valores das parcelas para todas as condicoes de Pagamento
que podem ser feitas para este produto, isso porque no Produto tem um atributo
quantidadeParcelas onde o sistema que este Produto só pode ser vendido em x vezes.

Entao eu preciso do Repositorio de CondicoesPagamento para pegar todas condicoes de
pagamento com parcelas menor ou igual às daquela configurada no Produto,
além disso tenho que agrupar por portador entao tbm preciso do Repositorio deste.

É algo mais ou menos assim, o Produto custa R$200 reais o usuario veria isso>

Boleto
à vista 200

Cartao Master
1 * 200
2* 100
3* 67
4 * 55 //juros de 10 porcento configurada na CondicaoPagamento
etc

Cartao Visa

Na minha opinião não seria boa coisa colocar as formas de pagamento na classe de produtos:

  1. Boas praticas nos ensinam a programar com classes POJO´s, acho que seria melhor manter a classe produto assim;
  2. Coloque as regras de negocio em uma classe de serviço, nela você poderia ter o metodo: listarFormaDePagamentos,
    que exibiria a lista de condições de pagamento validas;

Se você colocar formas de pagamento na classe de produto, no caso de existir mais de um produto selecionado, como se iria decidir quais exibir, poderia acontecer casos em que haveriam contradições entre elas, um produto poderia se vendido em 10x outro em 12x.

Espero ter ajudado

O que você quis dizer exatamente com isso? Pra ter entidades apenas com getters e setters?

Se foi isso, reveja seu conceito de “boas práticas”, pois entidades burras só levam ao desenvolvimento procedural como é utilizado por boa parte dos programadores java.

Quanto ao post, danielbussade, dê uma olhada nesse post traz algumas sugestões para um questionamento semelhante ao seu.

[]'s Hewerton Crisóstomo

No post que você citou vai ver que varias vezes foram citadas a palavra POJO e que em nenhuma delas disseram que pojos eram classes burras. O post começa com o Rubem Azenha pedindo opinião para mudar uma implementação usando uma classe de serviço noticiaService.publica(noticia)
para uma implementação seguindo os principios do DDD. No Java sempre temos mais de uma forma de fazer as coisas (parece até Neston), só pra citar um exemplo, temos os frameworks MVC, temos varios:

JSF;
STRUTS;
Spring MVC… etc. Somente para citar alguns.

Todos eles tem os seus prós e contras dependendo das suas necessidades um atende melhor que o outro.

A sugestão que lhe passei seria usando classes POJO´s + classes de Serviço (programação procedural???), mais não tome isso como uma verdade absoluta.
Cara numa boa, não sei quanto tempo você ja tem de experiencia, mais como na sua resposta você tambem ja fez uma avaliação da maioria dos programadores Java, suponho que seja muita, mais terá mais ajuda se for menos groseiro, poderia responder que queria uma solução baseada em DDD e talvez até citando o post, mais volto a afirmar dei uma sugestão, não precisa cortar os pulsos… rsrsrs

Alias a ultima resposta sugere a utilização de IOC deixando bem separada as responsabilidades entre as classes, oque eu tambem concordo.

[quote=fernandojsjr]

Se você colocar formas de pagamento na classe de produto, no caso de existir mais de um produto selecionado, como se iria decidir quais exibir, poderia acontecer casos em que haveriam contradições entre elas, um produto poderia se vendido em 10x outro em 12x.

Espero ter ajudado[/quote]

Fernando, esta contradicao quem resolveria e a classe Sacola, onde ele faz um for em todos os ItensSacola
e invoca o metodo do Produto chamado getMenorQuantidadeParcela, que não só consulta os produtos
mas tbm os grupos e subgrupos dos mesmos, sempre se decidindo pela menor parcela.

Olhe.
A minha opinião para a sua primeira pergunta: a primeira abordagem. Isso é injetar, não é?

Agora uma pergunta: você vai usar save, delete, update na classe Produto? Digo, produto.save() ou algo do tipo? Se sim, eu desaconselho a usar isso (pode ser chamado de ActiveRecord e funciona perfeitamente com Rails, mas com Java acho que não casa).

A minha opinião para o seu problema: eu colocaria isso de calcular o preço em todas as condições em uma outra classe. Alguma classe que juntasse os dois, Produto e CondicaoDePagamento (pode ser Sacola será?). Porque pense: qual a relação de um com outro? Digo… Usar produto.calcularValorDoProdutoNaFormaDeBoleto(), produto.calcularValorDoProdutoNaFormaDeMasterCard(), … pode ficar muito pouco flexível. E se surge um cartão, JavaCardMasterSuperMoney? Você tem que alterar em produto e isso pode não ser tão interessante.

O que você poderia fazer é criar uma nova classe que armazena os preços (isso já pode ficar em ItemSacola, que pode ter uma referência pra produto. Aí Sacola calcula os preços de acordo com Condicao e seta em ItemSacola). Então você passa pelo for de todas as CondicoesDePagamento possíveis e vai criando objetos dinamicamente. Você nunca mais vai precisar se preocupar em implementar novos métodos, porque todo o cálculo vai ser baseado nas propriedades de CondicaoDePagamento.

Será que pode ser feito assim? Será que vai ficar acoplado demais? Só pegando outras opiniões e detonando essa arquitetura pra ver qual forma fica mais interessante. De qualquer forma, vamos esperar a opinião dos mais experientes, certo?

No construtor com auto injeção.

class RepositorioProduto .. { Produto novoProduto() { return new Produto(this); } }

[quote=Andre Brito]Olhe.
A minha opinião para a sua primeira pergunta: a primeira abordagem. Isso é injetar, não é?

Agora uma pergunta: você vai usar save, delete, update na classe Produto? Digo, produto.save() ou algo do tipo? Se sim, eu desaconselho a usar isso (pode ser chamado de ActiveRecord e funciona perfeitamente com Rails, mas com Java acho que não casa).

A minha opinião para o seu problema: eu colocaria isso de calcular o preço em todas as condições em uma outra classe. Alguma classe que juntasse os dois, Produto e CondicaoDePagamento (pode ser Sacola será?). Porque pense: qual a relação de um com outro? Digo… Usar produto.calcularValorDoProdutoNaFormaDeBoleto(), produto.calcularValorDoProdutoNaFormaDeMasterCard(), … pode ficar muito pouco flexível. E se surge um cartão, JavaCardMasterSuperMoney? Você tem que alterar em produto e isso pode não ser tão interessante.

O que você poderia fazer é criar uma nova classe que armazena os preços (isso já pode ficar em ItemSacola, que pode ter uma referência pra produto. Aí Sacola calcula os preços de acordo com Condicao e seta em ItemSacola). Então você passa pelo for de todas as CondicoesDePagamento possíveis e vai criando objetos dinamicamente. Você nunca mais vai precisar se preocupar em implementar novos métodos, porque todo o cálculo vai ser baseado nas propriedades de CondicaoDePagamento.

Será que pode ser feito assim? Será que vai ficar acoplado demais? Só pegando outras opiniões e detonando essa arquitetura pra ver qual forma fica mais interessante. De qualquer forma, vamos esperar a opinião dos mais experientes, certo?[/quote]

}

Na verdade a funcionalidade é feita antes do Produto ser colocado na sacola, além disso eu nao me acopla a nenhum Portador especifico como voce disse, o que eu faço é recuperar todas as COndicoes com numero de parcelas menor ou igual a configurada no produto entao faco um for nestas condicoes e chamo um metodo da classe CondicaoPagamento chamado :

calculaValorParcelaParaTotal(Number total)

Onde se tiver juros ela ja aplica e juros e divide pela sua quantidade de parcelas, depois o que faço é agrupar isso para todos os portadores,com excecao dos que sao vendidos somente avista como boleto, e quem me diz isso e a propria classe Portador atraves de um metodo

boolean isSomenteAVista();

Acesse www.crisroli.com.br, é um de nossos clientes de e-commerce, clique em um produto e veja na parte de baixo a funcionalidade da qual falei.

Valeu

Obs:

Concordo com a opinião do Andre Brito, acho que você esta muito preocupado em não ter classes “burras” e esta querendo modelar classes com responsabilidades demais. Vamos aproximar este modelo do mundo real, em uma loja você pega um produto e “pergunta” e/ou “sabe” a condição de pagamento dele. Essa resposta só é dada no caixa na hora do pagamento, normalmente tendo como base o valor total da compra, bandeira do cartão, a vista, ou aprazo, promoção de aniversário da loja, etc.

oops, me referi a classes burras citando o exemplo do Javabuntu

[quote=Andre Brito]Olhe.
A minha opinião para o seu problema: eu colocaria isso de calcular o preço em todas as condições em uma outra classe. Alguma classe que juntasse os dois, Produto e CondicaoDePagamento (pode ser Sacola será?). Porque pense: qual a relação de um com outro? Digo… Usar produto.calcularValorDoProdutoNaFormaDeBoleto(), produto.calcularValorDoProdutoNaFormaDeMasterCard(), … pode ficar muito pouco flexível. E se surge um cartão, JavaCardMasterSuperMoney? Você tem que alterar em produto e isso pode não ser tão interessante.
[/quote]

Concordo. Penso ser indicado criar classes que realizam o caso de uso (sacola ?) e colocar as responsabilidades específicas nelas.

Do contrário, a entidade (Produto) tende a ter responsabilidades demais… Expandindo um pouco o domínio, imagine que no futuro você precise saber o histórico de compras de um produto, quem são os clientes que o compraram, o giro médio, …

Quanto aos testes, utilizo uma classe utilitária que injeta o mock na força bruta via reflection.

Abraços

[quote=danielbussade]Na verdade a funcionalidade é feita antes do Produto ser colocado na sacola, além disso eu nao me acopla a nenhum Portador especifico como voce disse, o que eu faço é recuperar todas as COndicoes com numero de parcelas menor ou igual a configurada no produto entao faco um for nestas condicoes e chamo um metodo da classe CondicaoPagamento chamado :

calculaValorParcelaParaTotal(Number total)

Onde se tiver juros ela ja aplica e juros e divide pela sua quantidade de parcelas, depois o que faço é agrupar isso para todos os portadores,com excecao dos que sao vendidos somente avista como boleto, e quem me diz isso e a propria classe Portador atraves de um metodo

boolean isSomenteAVista(); [/quote]
Opa… Aí o negócio já muda. O método calculaValorParcelaParaTotal(Number total) é interessante deixar na CondicaoDePagamento mesmo, porque ela conheça as informações e vai retornar um valor. Achei bem interessante e válido.

Mas depois que você diz isso, qual a sua principal dúvida? Se você deve deixar essa regra de chamar condição em Produto? Eu acho que não teria muito a ver porque Produto não conhece CondicaoDePagamento, e nem o contrário acontece. Um Produto é um produto e não tem nada a ver com o pagamento, é simplesmente um cadastro. Mesma coisa com CondicaoDePagamento.

Acho que desvirtuamos um pouco do tópico… Se fosse pra colocar o Repository dentro da Entity e não fosse pra usar isso que falei acima, colocaria pelo construtor. Se não der, acho que seria interessante você fazer o que o Robson Farias disse. Não tinha pensado daquela forma. Veja que você vai manter o construtor default pro Hibernate não reclamar. Só tem que se lembra sempre de dar o novoProduto().

Achei bem legal isso… Tá de parabéns, Robson :stuck_out_tongue:

Então André, a minha dúvida é exatamente essa se isso é ou não responsabilidade do Produto. Ao meu modo de ver não é pois ele fere o SRP www.objectmentor.com/resources/articles/srp.pdf , todavia não sei onde mais colocar essa regra, mas ela é uma junção de responsabilidades, tipo a CondicaoPagamento faz sua parte fornecendo o valor da Parcela, o Produto deve lhe passar o seu preço, e depois tenho que agrupar isso por Portador.

O grande incoveniente é ficar dentro do método do Produto recuperar CondicoesPagamento e Portadores do meu Repositorio,
embora eu tenha enxugado bem os método fazendo Refactor com extract Method ainda ficou com declaracoes horriveis, algo como

Map<Portador, List<Map<CondicaoPagamento,Float>>

Ou seja para cada portador, eu tenho uma lista de mapas de CondicoesPagamento e seu respectivo valor, até pensei em extrair isso para uma Classe, mas além de ser uma classe de nome difícil, ainda teria que ter o mapa, mas para algo reduzido assim:

Map<Portador, ValorParcelaPorCondicaoPagamento>

O que acha da criacao dessa classe? Acha válido? Acha que enriquece o modelo?

Quanto o problema do Repositorio irei colocá-lo no Construtor, resolvi o problema da IoC
com AOP, criando pointcuts e injetando depois do new.

Valeu

Obrigado

[quote=Andre Brito][quote=danielbussade]Na verdade a funcionalidade é feita antes do Produto ser colocado na sacola, além disso eu nao me acopla a nenhum Portador especifico como voce disse, o que eu faço é recuperar todas as COndicoes com numero de parcelas menor ou igual a configurada no produto entao faco um for nestas condicoes e chamo um metodo da classe CondicaoPagamento chamado :

calculaValorParcelaParaTotal(Number total)

Onde se tiver juros ela ja aplica e juros e divide pela sua quantidade de parcelas, depois o que faço é agrupar isso para todos os portadores,com excecao dos que sao vendidos somente avista como boleto, e quem me diz isso e a propria classe Portador atraves de um metodo

boolean isSomenteAVista(); [/quote]
Opa… Aí o negócio já muda. O método calculaValorParcelaParaTotal(Number total) é interessante deixar na CondicaoDePagamento mesmo, porque ela conheça as informações e vai retornar um valor. Achei bem interessante e válido.

Mas depois que você diz isso, qual a sua principal dúvida? Se você deve deixar essa regra de chamar condição em Produto? Eu acho que não teria muito a ver porque Produto não conhece CondicaoDePagamento, e nem o contrário acontece. Um Produto é um produto e não tem nada a ver com o pagamento, é simplesmente um cadastro. Mesma coisa com CondicaoDePagamento.

Acho que desvirtuamos um pouco do tópico… Se fosse pra colocar o Repository dentro da Entity e não fosse pra usar isso que falei acima, colocaria pelo construtor. Se não der, acho que seria interessante você fazer o que o Robson Farias disse. Não tinha pensado daquela forma. Veja que você vai manter o construtor default pro Hibernate não reclamar. Só tem que se lembra sempre de dar o novoProduto().

Achei bem legal isso… Tá de parabéns, Robson :p[/quote]

Opa. Não consegui acessar o site (nem copiando e colando). Se puder anexar no seu post seria legal.
De qualquer forma, também acho que não é responsabilidade da classe Produto. Acho que deve ter alguma aí no meio pra fazer isso (não uma classe de serviços, mas algo do gênero).

[quote=danielbussade]Ou seja para cada portador, eu tenho uma lista de mapas de CondicoesPagamento e seu respectivo valor, até pensei em extrair isso para uma Classe, mas além de ser uma classe de nome difícil, ainda teria que ter o mapa, mas para algo reduzido assim:

Map<Portador, ValorParcelaPorCondicaoPagamento>

O que acha da criacao dessa classe? Acha válido? Acha que enriquece o modelo?[/quote]
Acho válido sim… Quem sabe essa classe poderia ter uma referência pra Produto e CondicaoDePagamento. Acho que poderia casar… A priori não consigo ver os problemas, mas sempre surge algum ou outro.