quinta-feira, 14 de outubro de 2010

Habilitando dominios ricos no SEAM

quinta-feira, 14 de outubro de 2010
A metodologia DDD enfatiza que ao desenvolvermos as regras de negócios devem ficar na camada de domínio, concentrando todo os relacionamentos e o funcionamento do sistema nesta camada, portanto poderíamos ter um método como:

cliente.pagar(faturaEmAtrazo);
Cliente é uma entidade do sistema, com isso não quero dizer que o cliente deve ter a lógica de pagamento de faturas, mais é ele quem inicia o processo, de onde então vem a lógica? De uma service:
public class ControleDeFaturas {
  public void receberPagamento(Cliente cliente,Fatura fatura){
    .... // aqui residiria a lógica de 
  }
}
e o cliente como fica? Ele recebe por Dependency Injection o serviço de controle de faturas, usando o framework seam teriamos algo como:
@Entity
public class Cliente {

  @In
  private ControleDeFaturas controleDeFaturas;

  ...

  public void pagar(Fatura fatura){
    controleDeFaturas.receberPagamento(this,fatura);
  }

}
Assim o modelo fica melhor, agora um problema que temos no SEAM é que ele não faz injeção de dependências nas entitys, para resolver este problema temos várias soluções, poderíamos usar a entity com uma factory que receberia o controleDeFaturas, ou mesmo fazer lookup da dependência usando Component.getInstance("controleDeFaturas"), uma abordagem que usei a qual estou gostando é o uso de Aspectos, aspecto resolve este problema, de uma forma transparente, onde mesmo se instanciarmos o Cliente por meio de um new Cliente(); ainda teríamos a injeção da dependência.

você pode visualizar a classe aqui que resolveu meu problema (usa aspectj).


Com certeza a classe pode ser melhorada, mas inicialmente resolveu o problema e permitiu uma habilitação de um domínio rico usando o SEAM, basicamente ele injeta uma instancia sempre que encontra uma classe com a anotação @Entity e um @In tanto faz ser nos atributos da classe, ou nos métodos acessores.

Devo agradecer ao Alessandro Lazarotti que sugeriu o aspecto acima como solução no fórum do guj.


Nenhum comentário :

quarta-feira, 6 de outubro de 2010

Fluent Interfaces e operadores Lógicos

quarta-feira, 6 de outubro de 2010
Algo com que todos os desenvolvedores tiveram como problema é a multiplicade de operações repetidas, creio que um exemplo seja melhor.

Reparem neste trecho de código:
public class LocalizadorDeClientes{
    public List<cliente> buscarPorCPF(String cpf){
        //implementação da busca.
    }
    
    public List<cliente> buscarPorNome(String nome){
        // implementação da busca.
    }
}
O cliente solicita agora que a busca seja feita por cliente inadimplente, a implementação mais comum seria mais um método de pesquisa:
public List<cliente> buscarInadiplentes(){
...
}
Essa abordagem é ruim? Não necessariamente, mas podemos usar um esquema diferente, o cliente te solicita agora quero combinar os filtros, muitos diriam que não dá ou fariam métodos diversos para todas as combinações possíveis.

Para casos como estes em que queremos combinar comportamentos poderemos tirar proveito da fluent interfaces:

Os exemplos apresentados aqui foram feitos apenas para exemplificar a metodologia podendo variar em implementação.

Reparem no exemplo modificado, usando fluent interfaces:
public class LocalizadorDeClientes{
public LocalizadorDeClientes porCPF(String cpf){
//configura a busca para ser realizada por cpf
return this;
}

public LocalizadorDeClientes porNome(String nome){
//configura a busca para ser realizada por nome
return this;
}

public LocalizadorDeClientes inadimplente(){
//configura a busca para ser realizada por inadimplentes
return this;
}

public List<cliente> fazerBusca(){
//configura a busca para ser realizada por inadimplentes
return this;
}

}
o uso seria assim:
LocalizadorDeClientes buscador = new LocalizadorDeClientes();
buscador.PorCPF(numCPF).porNome("carlos").inadimplente().fazerBusca();
Reparem que agora você pode encadear a chamada, fazendo com que a busca por exemplo possa ser configurável.

Este exemplo pode ser estendido usando um esquema de operadores lógicos, bastaria adicionar os métodos na classe:
public LocalizadorDeClientes e(){
// configura a busca para executar com o operador e
// no hibernate seria um conjunction em memória seria um &&
return this;
}

public LocalizadorDeClientes ou(){
// configura a busca para executar com o operador ou
// no hibernate seria um disjunction em memória seria um ||
return this;
}

public LocalizadorDeClientes nao(){
// configura a busca para executar com o operador nao
// no hibernate seria um Restriction.not em memória seria um !
return this;
}
Agora poderia fazer as consultas por exemplo assim:
LocalizadorDeClientes buscador = new LocalizadorDeClientes();
buscador.PorCPF(numCPF).e().porNome("carlos").nao().inadimplente().fazerBusca();
um esquema como este de fluent interfaces permite compor comportamento complexo a partir de um conjunto de comportamentos simples, a projetos que já utilizam isso como o Calopsita isso foi até mesmo discutido recentemente o Tectura no tópico nomes Complexos/descritivos no modelo e Martin flower fala exatamente sobre isso em seu artigo sobre fluent interfaces.

Tente entender o que é fluent interfaces, e o seu uso com operadores lógicos, pois ajudam a ter um domínio mais rico em seu software.


Nenhum comentário :