Tag: Regras de Negócio
DDD encapsula a minha Regra de Negócio
por Rodrigo Allemand em Oct.16, 2009, em DDD
Resolvi responder ao comentário do Diogo (no post Blindando o DomÃnio) em um post separado por achar que essa é a duvida mais frequente que eu vejo, seja em conversas informais, fóruns, palestras, etc.
Onde colocar as minhas regras de negócio?
A resposta é…. Depende! rs
Se você escolheu utilizar DDD no seu projeto é porque você conhece e já isolou o problema a ser resolvido que, por ser complexo, será baseado em um modelo, recebendo o foco e a prioridade necessárias para a solução deste domÃnio. Quando você isola este domÃnio, obviamente você deve encapsular tambem todas as regras de negócio dentro desse domÃnio.
Para exemplificar melhor vamos fazer a seguinte funcionalidade de exemplo no nosso dóminio.
Todos os pedidos com valor acima 1.000,00 e que são pagos com dinheiro em espécie são elegÃveis a um desconto de 10% por produto.
Claro que esta informação tem que ser passada para o usuário final do sistema, mas é responsabilidade do sistema checar se as informações passadas estão corretas. Você pode até colocar a regra de validação tambem na camada de apresentação ou dentro do seu banco de dados em forma de triggers (argh, rs!), mas de qualquer maneira o domÃnio tem que ter esta validação, já que um dos princÃpios do DDD é justamente poder trocar a camada de apresentação sem que o domÃnio seja alterado ou compartilhar o mesmo domÃnio entre interfaces/sistemas diferentes.
Portanto, segue a primeira afirmação:
Por mais que a sua camada de apresentação faça as regras necessárias para uma determinada funcionalidade, o seu domÃnio tambem deve, obrigatoriamente, fazer com que estas regras estejam presente internamente, tornando-o coeso da camada que ele comunica.
Vamos melhorar este exemplo mostrando como ficariam as entidades envolvidas nesta funcionalidade.
public class Pedido {
private List<ItemPedido> itens;
private BigDecimal precoTotal;
//Acessores omitidos
}
public class ItemPedido {
private Produto produto;
private BigDecimal precoVenda;
private BigDecimal desconto;
//Acessores omitidos
}
OBS.: Muitos podem perguntar porque a informação referente ao preço final é um atributo da classe e não um comportamento da entidade (um método que poderia somar todos os preços finais decrementando os descontos dados por cada produto, multiplicado pela quantidade de cada produto). Vamos pensar que o preço do Produto pode – e deve – variar e o sistema precisa manter a informação do preço de venda naquele momento, ok?
Eu encaro que esta regra de negócio deve estar em uma checagem no momento da inclusão, como por exemplo uma Specification destinada somente a esta regra. Lembro que, a Specification tem, neste caso, o intúito de validar uma informação e que ela não precisa ser necessariamente apenas uma única specification. Eu posso – e é até melhor – dividir a informação em várias Specifications, uma para cada regra de negócio que a minha funcionalidade exige.
Vamos pensar então que a minha classe ValidarDescontoDadoSpecification tem o seguinte código:
public class ValidarDescontoDadoSpecification
implements Specification<ItemPedido> {
public static final BigDecimal DESCONTO_MAXIMO_PGTO_DINHEIRO = 10;
public boolean isSatisfiedBy(ItemPedido item){
if(itemPedido.getFormaPagamento.equal(FormaPagamento.DINHEIRO){
BigDecimal descontoDado =
Calculadora.percentualEntre(
item.getPrecoVenda(),
item.getDesconto());
if(descontoDado.compareTo(DESCONTO_MAXIMO_PGTO_DINHEIRO) > 1){
//É necessário comunicar ao domÃnio o porque da falha
//Neste caso, leia mais sobre Notification
return false;
}
}
return true;
}
}
Pensando que você sempre salva o pedido, poderiamos ter uma outra Specification para isso, conforme abaixo:
public class PersistirPedidoSpecification
implements Specification<Pedido> {
public boolean isSatisfiedBy(Pedido pedido){
/*
As boas maneiras de aplicativos internet diz que
vc deve validar tudo de uma unica vez, rs
*/
boolean resultado = true;
//Validações referente ao Pedido
ValidarDescontoDadoSpecification spec =
new ValidarDescontoDadoSpecification();
for(ItemPedido item : pedido.getItens()){
if(!spec.isSatisfiedBy(item)){
/*
Lembre que a informação da falha deve ser feita
pela outra Specification, não por esta!
*/
resultado = false;
}
}
return resultado;
}
}
Assim, no final de tudo, nós poderiamos ter no repositório Pedidos, o seguinte código:
public class Pedidos{
public static void persistir(Pedido pedido){
PersistirPedidoSpecification spec
= new PersistirPedidoSpecification();
if(spec.isSatisfiedBy(pedido){
dao.salvar(pedido);
} else {
//Comunica a falha encontrada às outras camadas
}
}
}
Espero ter ajudado!!!
Abraço a todos!!!
Comente e Recomende!!