14
respostas

Seria correto implementar regras de negócio em classes de entidades/modelos de banco de dados?

Pessoal, seguindo o exemplo de NotaFiscal apresentado no vídeo de encapsulamento, vamos supor que esta seja uma classe de entidade do JPA para eu implementar o método calculaValorImposto() eu teria que marcá-lo como @Transient.

NotaFiscal nf = new NotaFiscal();
double valor = nf.calculaValorImposto()

Minha dúvida surge neste ponto, para todos os métodos que eu encapsular em uma entidade eu vou ter que marcá-los como transient, ou seria melhor ter duas classes, uma NotaFiscalEntity que teria apenas os get/set necessários do JPA e outra classe NotaFiscal com os demais métodos?

Obrigado!

14 respostas

Olá Danilo tudo bem?

Você não precisa marcar o método como transient, métodos dentro de uma entidade da JPA não são mapeados para o banco.

Espero ter ajudado e bons estudos.

Olá Fernando, obrigado pela resposta. Mas minha dúvida, até nem tem tanta relação com o Transient, seria mais em ter métodos de negócio em classes de entidade. Essa já é uma dúvida antiga que eu tenho, e está diretamente relacionada com conceitos de Anemic model e Rich model (http://blog.caelum.com.br/o-que-e-modelo-anemico-e-por-que-fugir-dele/).

Acho um pouco estranho ter lógica dentro de modelos, e não sei se isto é comum em outros lugares.

Fala aí Danilo,

Então o fato de você ter lógicas/comportamentos dentro do seu modelo é o que faz ele não ser um modelo anêmico. Tendo comportamento ele deixa de ser somente uma casca para dados.

DDD prega bastante isso de ter nossas lógicas no modelo/domínio, afinal faz parte dele.

Um exemplo bem básico/clássico seria uma conta bancária. Quais os comportamentos uma conta bancária deve ter?

  • Sacar
  • Depositar
  • Transferir

Não faz sentido eu criar uma classe com esses comportamentos que dependa da classe conta para executar esses comportamentos. Por isso colocamos esses comportamentos dentro da conta.

Espero ter ajudado e bons estudos.

Obrigado pela resposta Fernando, ajudou a entender melhor o assunto, principalmente quando decidir se um código de lógica de negócio vai para uma camada de business ou se fica na entidade (acredito agora que se o comportamento É daquele objeto, ele deve ter a implementação nele mesmo, agora se o comportamento ENVOLVE aquele objeto, fica numa camada de negócio).

Fernando, mas no caso específico da JPA isso pode ser problemático, não? Quero dizer, e se dentro de um método de uma classe de entidade eu tiver que usar uma regra de negócio que por sua vez tem que invocar um EntityManager?

Então Paulo, acho que não é um problema, pois você poderia receber o entityManager como parâmetro do método da regra de negocio.

Mas se esta envolvendo o EntityManager provavel que envolva mais de uma entidade, nesse caso pode ser avaliado o uso de Services por exemplo.

Entity Manager já é um conceito mais ligado à persistência, o mais correto seria receber um repositório na classe de entidade, pois um repositório é um conceito do negócio, correto?

o problema é justamente receber esse repositório de uma forma não intrusiva nas entidades JPA, se você vasculhar o Guj por exemplo,você vai encontrar bastante coisa sobre o assunto, porém na parte de como receber esse repositório na entidade quase ninguém concorda com as soluções propostas.

Eu já precisei fazer algo do tipo, e como uso EJB, a solução que não ficou bonita, mas funcionou pra mim, foi fazer o lookup JNDI do SessionBean dentro da classe de entidade

Pois é. Eu quis dizer que se é responsabilidade da classe de entidade Funcionario o método salvar() em algum momento eu vou ter que "poluir" o código com implementação JPA. Se não me engano existem restrições técnicas sobre isso. Daí o uso dos DAO's. Saliento que sou bem "verde" no assunto em geral e tenho me virado de forma auto-didata. Aqui pra mim é uma oportunidade pra espiar experiências reais de vocês e tentar entender. Esse caso que você citou foi bem revelador pra mim. Usar JNDI.

esse lance da própria entidade se salvar é o padrão active record, eu não gosto de active record com java. Esses métodos de salvar, remover, etc.. eu coloco nos repositórios

sobre o exemplo doJNDI que falei: https://cursos.alura.com.br/forum/topico-usando-entidades-que-tem-comportamentos-que-dependem-de-acesso-a-banco-com-ejb-18145

se vc estiver usando CDI dá pra fazer lookup dos repositórios tbm

Obrigado, Ricardo. Pra mim que só tenho desenvolvido projetos de estudo (e sozinho em casa) é muito importante poder espiar snippets de códigos do mundo real. Você aplicou a sugestão ali, injetando o SessionBean no método? Eu li autores dizendo pra usar classes "anêmicas" pra entidades pra evitar dores de cabeça e deixar o comportamento todo em services, mas aqui e ali não resisti à tentação de colocar comportamento em métodos @Transient das entidades. Parecia fazer muito mais sentido e os services ficam bem mais limpos e claros.

pois é Paulo, eu também costumo usar métodos com @Transient em minhas entidades para alguns comportamentos mais simples, por exemplo, se vc tem uma classe Abastecimento e nessa classe vc tem o valor que pagou no litro de combustivel e a quantidade de litros, se vc quiser saber o total que pagou vc não vai colocar esse código dentro de um service, vc vai colcar um método dentro da classe de entidade, anotar com Transient e devolver uma simples multiplicação.

Nos services eu costumo utilizar em métodos transacionais onde estão envolvidas várias entidades e necessitam de uma certa orquestração ou fazer alguma validação mais avançada.

Esse lance lá de usar acesso a banco de dados dentro da classe de entidade(meu lookup lá), só utilizei em casos bem específicos

Paulo, olha essa minha outra dúvida que tive, o Alberto me alertou justamente sobre isso, a service deve conter a orquestração, quem deve manter o estado é o próprio objeto. Por exemplo, na minha classe MateriaService por exemplo eu faço assim:

public Materia salvarMateria(Materia materia, Usuario usuario){
        boolean diarioAberto = diariosRepository.diarioEstaAberto(materia.getDiario().getDiarioId());
        if(!diarioAberto){
            throw new RuntimeException("Esta edição do diário já fechou ");
        }
        materia.setMatriculaInclusao(usuario.getMatricula());
        materia.setSetorOrigem(usuario.getSetor());
        materiasRepository.salvarMateria(materia);
        return materia;
    }

pra manter este código coeso ele deveria estar assim

public Materia salvarMateria(Materia materia, Usuario usuario){
        boolean diarioAberto = diariosRepository.diarioEstaAberto(materia.getDiario().getDiarioId());
        if(!diarioAberto){
            throw new RuntimeException("Esta edição do diário já fechou ");
        }

materia.atribuirUsuario(usuario);
        materiasRepository.salvarMateria(materia);
        return materia;
    }

e na classe Materia

public Class Materia(){
//outros atributos e métodos

public void atribuirUsuario(Usuario usuario){
   this.matriculaInclusao(usuario.getMatricula());
    this.setorOrigem(usuario.getSetor());
}

}

Obrigado, Ricardo. :)