Rodrigo Allemand

Arquivo de October, 2009

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!!

13 Comentários :, , mais...

Procurando por algo?

Use o campo abaixo para procurar por todo o site:

Ainda não achou? Deixe um comentário ou me mande um email que eu cuido disso!

Minhas indicações!

Alguns blogs que eu recomendo...