DDD
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!!
DDD – Fabricando e Encontrando Objetos
por Rodrigo Allemand em Aug.17, 2009, em 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 é:
- Criar objetos
- Encapsular complexidades de criação
- Reter objetos criados
- 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!
DDD – Blindando o DomÃnio
por Rodrigo Allemand em Mar.05, 2009, em DDD
Lembram que nesse post eu falei que o domÃnio deveria ser fechado e bem definido? Então! A melhor maneira de fazer isso é criar uma camada que faça uma “blindagem” no domÃnio, definindo o acesso a ela e validando contratos para os métodos.
Contrato nada mais é do que vc garantir que o que entra no domÃnio está valido e o que sai está de acordo com o que o método se propõe a fazer! Retirado do Wiki do Phillip…
Se quem chamar me garantir a pré-condição, eu garanto a pós-condição.
Garantir a pré-condição é garantir que todos os parâmetros que vc precisa (e recebe) para efetuar uma ação estejam de acordo com uma especificação mÃnima. Com isso, a pós-condição – que é a sua resposta do método (retorno) – estará garantida.
O padrão utilizado neste caso – para criar uma camada de comunicação do domÃnio com o mundo – é o Facade (leia-se “Fasseide”, “conjuncão de Face + ade”), um padrão de projeto citado no livro Design Patterns: Elements of Reusable Object-Oriented Softwares, da Gang of Four [Gof]. Ele cita o seguinte:
Fornece uma interface unificada para um conjunto de interfaces em um subsistema. Façade define uma interface de nivel mais alto que torna o subsistema mais facil de ser usado.
A motivação básica desse padrão é dividir sistemas em subsistemas para reduzir a complexidade do código.
Como eu falei anteriormente, um padrão pode ser desenvolvido de diversas maneiras, mas algumas implementações são tão simples que são frequentemente copiadas ao longo dos projetos. A implementação mais comum de Facade é ter um construtor privado – para impossibilitar a criação de uma instancia da classe - e colocar os métodos da facade como estáticos. Veja o exemplo:
public class Facade {
   private Facade(){
   }
   public static String sayHello(){
      return "Hello World!";
   }
}
Dentro do seu método, vc pode perfeitamente trabalhar da maneira que melhor imaginar, porem, não convem colocar nenhuma regra de negócio ou validação dentro da facade, já que um método interno do domÃnio pode referenciar outro método do domÃnio sem necessariamente passar pela facade.
Existem tambem quem utilize uma classe para fachada, criando outras classes mais especificas para controlar cada ação, dividindo o sistema em sub-dominios, chamados por uma mesma fachada.
Blindar e validar sem ter regra de negócio? Para organizar melhor esta bagunça, Martin Fowler e Erick Evans desenvolveram o padrão Specification.
[...] business logic can be recombined by chaining the business logic together using boolean logic.
OBS.: Aqui está o PDF de Fowler sobre Specification.
Com a Specification, vc pode validar, selecionar e até mesmo construir objetos-modelo para satisfazer a sua regra de negócio. Mais uma vez, nenhum padrão tem uma implamentação fixa, todas podem variar, mas a mais utilizada para Specification em Java é a seguinte:
public interface Specification<T> {
   boolean isSatisfiedBy(T t);
}
No exemplo acima, ela retorna true ou false, caso o objeto passado esteja de acordo com a especificação. Esta especificação ainda pode ser de vários tipos, conforme alguns exemplos descrito abaixo:
public abstract class AndSpecification<T> implements Specification<T>{
   private final Specification<T> one;
   private final Specification<T> other;
   //Retorna true se o objeto passar pelas duas especificações
   public AndSpecification(Specification<T> one, Specification<T> other) {
      this.one = one;
      this.other = other;
   }
   public boolean isSatisfiedBy(T t){
      return one.isSatisfiedBy(t) && other.isSatisfiedBy(t);
   }
}
public abstract class OrSpecification<T> implements Specification<T>{
   private final Specification<T> one;
   private final Specification<T> other;
   //Retorna true se o objeto passar por uma das duas especificações
   public OrSpecification(Specification<T> one, Specification<T> other) {
      this.one = one;
      this.other = other;
   }
   public boolean isSatisfiedBy(T t){
      return one.isSatisfiedBy(t) || other.isSatisfiedBy(t);
   }
}
public interface SelectionSpecification<T> {
   //Filtra uma lista de acordo com uma especificação
   List<T> isSatisfiedBy(List<T> list);
}
Utilizando o nosso exemplo, vamos criar um exemplo para validar o cadastro de um usuário:
public class CadastrarUsuarioSpecification
   implements Specification<Usuario> {
   public boolean isSatisfiedBy(Usuario usuario) {
      if(usuario.getNome().equals("") ||
        usuario.getSenha().equals("") ||
        usuario.getLogin().equals("")){
         return false;
      }
      return true;
   }
}
OBS.: Lembrando que isso é apenas um exemplo didático, a entidade usuário do exemplo tem mais campos para serem validados!
O Dominio deve ter a sua validação independente do que vc utilize fora dele, seja Client-side com JavaScript ou Server-Side no seu servlet, por exemplo. Sempre faça o Design By Contract valer no seu domÃnio.
A especificação CadastrarUsuarioSpecification deve executar todas as validações necessárias para a inclusão de um usuário na base de dados. Utilizando-a em conjuntio com a Facade, teriamos algo como:
public class Facade {
   private Facade(){
   }
   public static void cadastrar(Usuario usuario){
      CadastrarUsuarioSpecification spec =
         new CadastrarUsuarioSpecification();
      if(spec.isSatisfiedBy(usuario)){
         //Cadastrar usuario
      } else {
         //Levantar erro!
      }
   }
}
Com isso, isolamos o domÃnio, garantimos o contrato e o seu domÃnio está vacinado conta possiveis erros vindo de outras camadas!
P.S.: Para o conceito de Specification não ficar vago, ainda existem muitos modelos de se contruir uma especificação, seja ela para acesso a banco, para colecionar critérios, para gerar um SQL, para filtrar uma lista, etc. O que precisamos ter em mente é que sempre que vc quizer implementar uma regra de maneira clara, procure ver antes se ela se encaixa em uma specification.
Até!
comente e Recomende!