Rodrigo Allemand

DDD encapsula a minha Regra de Negócio

por Rodrigo Allemand em Oct.16, 2009, como 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!!

9 Comentários :, , mais...

O Cliente tem sempre razão… o usuário nem sempre!

por Rodrigo Allemand em Sep.24, 2009, como Matodologia Agil

Muitas coisas fazem um projeto passar de sucesso a um completo fracasso, dentre eles uma equipe mau escolhida, um escopo não casado com os prazos, uma arquitetura ruim, etc. Mas um dos maiores problemas que eu vejo que pode transformar um “felizes para sempre” em um “problema para sempre” é o levantamento das necessidades e requisitos.

Mais uma vez, muitos podem ser os fatores que atrapalham um bom levantamento. Analistas inexperientes, hierarquias mau definidas, credibilidade da equipe, clima hostil com o cliente, falha de detalhamento – muito ou pouco, dependendo do caso, usuários indecisos, usar “o que já existe” como exemplo e o pior de todos: usuários “acostumados” com o erro.

Recentemente, estou trabalhando como bombeiro em um projeto que espelha muito do dito acima.

A proposta era redesenhar um sistema originalmente feito em Delphi, que já funcionava a alguns anos para uma estrutura web em Java que temos. Este sistema já tinha passado por diverss atualizações, transformando o código em uma bela macarronada

Por uma briga que se arrasta a gerações (rs), há um clima hostil entre o gerente de projetos e o cliente final (que não cabe a mim questionar, muito menos entender). Isso gerava muitas discursões (infinitas até) quando se queria definir algum ponto.

Dica 1:Quando o manifesto ágil diz para ter o cliente como parte da equipe não é so ter o cliente por perto, mas sim jogando no mesmo time.

Quando se senta para definir alguma coisa, quanto mais gente entendendo, melhor! Entendendo não significa estar falando ou questionando. Mas todas pessoas precisam ter o mesmo entendimento. Por muitas vezes na reunião, surgia uma pergunta que não tinha fundamento algum, como se a pessoa estivesse voando ou não entendendo nada do que estava acontecendo.

Dica 2: Nunca deixe uma duvida no ar. A reunião é justamente para atender as necessidades dos analistas. Preste atenção quando alguem fala para poder perguntar alguma coisa válida. E questione se não entender! Sempre!

Para piorar, o gerente teve uma das piores atitudes que se pode tomar em uma reunião de levantamento: proibir os analistas de perguntarem, fazendo ele um funil com o cliente. Esta atitude fez com que conceitos fossem perdidos e o pior, conceitos errados surgiram.

Dica 3: Para um projeto, hierarquias não devem existir. Claro que algumas vozes pesam mais do que outras mas no momento de levantamento, todos devem ser ouvidos.

Então começa a definição dos casos de uso. Envolvidos em conceitos errados, climinhas e muita briga, os analistas ‘copiaram’ as funcionalidades do sistema antigo sem propor nenhuma alteração. Na aprovação do documento de requisito (aqui nós temos esse passo, por enquanto, rs) o usuário reclamou de muita coisa que estava definida ali, dizendo que se fosse par ter a mesma coisa, que continuasse com o antigo.

Dica 4: O legado pode ser usado como exemplo, mas tente sempre surpreenda o cliente com alguma novidade. Pense no que pode melhorar o trabalho dele e proponha modificações, sempre embasadas em alguma teoria, por mais simples que seja.

Troca-se a equipe para dar mais força ao projeto. Com isso, o usuário já não acreditava na equipe anterior, achando que eles poderiam novamente fazer as besteiras feitas anteriormente.

Dica 5: Por mais que fosse necessária a colocação de mais recursos, nunca deixe a credibilidade da equipe em risco. Se você não acredita no vendedor, como vai comprar o produto?

A nova equipe começou a detalhar – novamente – as funcionalidades e as mesmas tiveram variações, fazendo com que coisas com pouco detalhe tivessem uma pontuação de ‘codificação’ errada diante de tantos detalhes e outras que realmente tinham um alto grau de dificuldade, com pontuações baixas diante da falta de detalhamento.

Dica 6: Detalhe somente o necessário. Incremento é justamente isso! Se Quando chegar o momento você aumentará o nivel de detalhe.

Dá pra sentir como foi a fase de desenvolvimento deste projeto, né?!

Fazendo uma analogia com a construção de uma casa, a reunião de apresentação dos requisitos e das datas de entrega foi mais ou menos assim.

Analista: (…) Então aqui nós teremos os 3 quartos necessários.
Usuário: Mas eu não preciso de 3 quartos. Eu preciso de 3 camas!
Analista: Sim mas nós colocamos 3 quartos distintos para uma melhor modularização. Sem contar que esse quarto maior aqui é uma suite.
Usuário: Mas nesse quarto aqui, alem de ser suite, ele precisa de uma cozinha.
Analista: Como assim uma cozinha? Já existe uma cozinha aqui, veja!
Usuário: Mas eu preciso de uma cozinha aqui porque na antiga casa eu tinha uma cozinha perto da cama.
Analista: Sim, mas agora nós temos quartose a cozinha em ambientes separados.
Usuário: (…) E onde está a minha janela dos fundos para eu entrar?
Analista: Agora nós temos uma porta aqui nos fundos.
Usuário: Está errado. Funcionava assim: eu entrava na casa, abria a janela, saia da casa, dava a volta na casa, entrava pela janela e acessava a parte dos fundos da casa.
Analista: Agora você não vai precisar fazer isso. Nós temos uma porta para que você entre diretamente por ela, já que o proposito é apenas entrar na casa.
Usuário: Não o proposito é entrar na casa pela janela. E janela não é porta. A porta é pra entrar pela frente, a janela é para entrar pelos fundos.

Dica 6: Para o usuário que está acostumado com o erro do software antigo (que para ele já é trivial “entrar por uma janela”), você precisa mostrar e provar para o usuário que se é para entrar, precisa ser por uma porta.

Como dizia meu avô…

Burro não aprende, burro acostuma.
Não faça do seu usuário um burro.

Comente e Recomende!

3 Comentários :, , , mais...

DDD – Fabricando e Encontrando Objetos

por Rodrigo Allemand em Aug.17, 2009, como DDD

Antes de mais nada, este post não é – como nenhumk dos outros desta série – focado extritamente em DDD. Leia o primeiro post da séries para entender melhor…

Em qualquer sistema OO, vc trabalha com….. objetos (ohhhhhhhh)

Qualquer objeto ‘precisa’ ser….. criado antes de ser utilizado…. (ohhhhhhh)

Qual a melhor maneira de se criar objeto? (errr…. humm….. bem…….)

Muitas são as maneiras… usando um construtor, por DI (Dependency Injection), por fábricas, etc.

O mais utilizado são as fábricas, ou Factories conforme catalogado pelo GoF, ou Gang of Four.

Resumidamente, “o padrão Factory fornece uma interface para a criação de famílias de objetos correlatos ou dependentes sem a necessidade de especificar a classe concreta destes objetos”.

Diversos exemplos de fábricas existem espalhados pela internet, e eu não vou colocar mais um aqui. Procure, rs!

A motivação inicial do padrão era a criação de objetos complexos, fazendo com que a lógica estivesse toda em um unico lugar… princípio de DRY nos promórdios da Arquitetura de Software!

Porém, certos objetos alem de serem complexos, precisavam de um processo de construção demorado. Algumas tecnicas para a sulução deste problema era ‘reter’ o objeto contruido e utilizá-lo como um ponto de acesso único, otimizando assim o processo de construção.

Com isso, por muitas vezes os desenvolvedores juntavam dois padrões de projetos para sanar o problema de contrução complexa e demorada, utilizando Factory e ‘Singletons’ para colocar objetos a disposição do ‘domínio’.

Colocar fábricas a disposição dos desenvolvedores, por muitas vezes, mascarava onde realmente estes objetos deveriam ser criados. Poderiam existir inúmeras fábricas em um único projeto, como eu já presenciei num passado não muito distante. Era DaoFactory pra cá, AbstractDaoFactory pra lá, ReportFactory aqui e ServiceFactory acolá! Uma verdadeira macarronada de factories!

Então, como manter um ponto único de acesso para ‘encontrar’ os objetos?

Fowler resolveu colocar tudo em um unico padrão e o nomeou de Registry, ou Registro.

De acordo com Fowler, Registry é

Um objeto conhecido que outros objetos podem usar para encontrar objetos e serviços comuns.

A função do Registro é:

  1. Criar objetos
  2. Encapsular complexidades de criação
  3. Reter objetos criados
  4. Funcionar como ‘local dos objetos’

Com isso, se você quizer saber ou precisar de um objeto e o seu sistems tem um Registro, pergunte a ele!

Normalmente os Registros são factories e singletons mesclados, conforme o exemplo abaixo:

public MyDomainRegistry{
    private static MyDomainRegistry INSTANCE = null;

    private MyDomainRegistry(){
        loadVariables();
    }

    private static MyDomainRegistry instance(){
        //Coloque aqui a sua implementação de Singleton
        return INSTANCE;
    }

    //Posso usar SubDomains
    private ConsultarSubDomain consultarSubDomain;
    //Posso usar Services Services
    private ConversaoService conversaoService;
    //Qualquer objeto que eu queira localizar!
    private MyRepository myRepository;

    private void loadVariables(){
        //Aqui vc deve criar os seua objetos como achar melhor
        this.consultarSubDomain = new ConsultarSubDomain();
    }

    //Aqui vc coloca os métodos estáticos para retornar
    //os objetos que o registro precisa controlar
    public static ConsultarSubDomain consultarSubDomain() {
        return instance().consultarSubDomain;
    }
}

Bem simples, não?

Agora, para você utilizar um objeto, basta solicitar ao registro, assim.

public class FachadaDoDominio {

    private FachadaDoDominio() {
    }

    public static ConsultarSubDomain consultar(){
        return MyDomainRegistry.consultarSubDomain();
    }
}

Comente e Recomende!

Deixe um comentário :, , , , 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...