Este post faz parte de uma série sobre Spring, JPA e JSF. Caso algum
assunto abordado aqui não esteja claro, consulte este link: Spring + JPA.
Quando utilizamos Business Delegate (BD), temos como principal objetivo reduzir o acoplamento entre a camada de visão propriamente dita e os serviços. Neste exemplo que venho construindo desde alguns posts anteriores essa abordagem talvez não seja necessária, mas a aplicação já ficará preparada para evoluir, se for necessário.
Vamos criar um pacote chamado delegate e duas classes semelhantes às classes da camada de controle (ou serviços):
Quando utilizamos Business Delegate (BD), temos como principal objetivo reduzir o acoplamento entre a camada de visão propriamente dita e os serviços. Neste exemplo que venho construindo desde alguns posts anteriores essa abordagem talvez não seja necessária, mas a aplicação já ficará preparada para evoluir, se for necessário.
Vamos criar um pacote chamado delegate e duas classes semelhantes às classes da camada de controle (ou serviços):
package delegate;
import entidades.Entidade;
import java.io.Serializable;
import java.util.List;
import service.AbstractService;
public abstract class AbstractBD<T extends Entidade> {
protected AbstractService<T> service;
public abstract void setService(AbstractService<T> service);
public void salvar(T entidade) {
service.salvar(entidade);
}
public void salvarLista(List<T> lista) {
service.salvarLista(lista);
}
public T carregar(Class classe, Serializable codigo) {
return service.carregar(classe, codigo);
}
public List<T> listar(Class classe) {
return service.listar(classe);
}
public List<T> consultaPersonalizada(String consulta) {
return service.consultaPersonalizada(consulta);
}
public List<T> consultaNativa(String consulta) {
return service.consultaNativa(consulta);
}
}
package delegate;
import java.io.Serializable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import service.AbstractService;
@Component(value = "bd")
public class GenericBD extends AbstractBD implements Serializable {
@Override
@Value("#{genericService}")
public void setService(AbstractService service) {
this.service = service;
}
}
Não temos muito o que comentar aqui. Como aconteceu com as classes "Service", temos os métodos que serão utilizados, injeção de dependências dos componentes do Spring que serão necessários, e só. O único ponto relevante é o baixo acoplamento. Mudanças podem ocorrer nos serviços sem que a camada de visão sofra qualquer alteração, e vice-versa. Além do fato de que a camada de serviços trabalha com transações e a camada de visão não percebe isso. Esse é mais um motivo para ressaltar a importância da programação voltada a interfaces (ou voltada a classes abstratas).
Com essa tecnologia, podemos ter mais de um tipo de serviço (serviços para bancos de dados e serviços para arquivos xml, por exemplo) controlados por vários business delegates. Isso permitiria mudar a maneira como se faz a persistência dos dados sem modificar nada na camada de visão. Mas é preciso ainda mais um nível de desacoplamento que pode ser obtido implementando um facade (fachada), que será responsável por reduzir a complexidade dos BDs e fornecer uma classe com métodos que a camada de visão pode entender com maior facilidade. Por exemplo, com base em uma escolha do usuário ou por meio de um arquivo de configuração, o facade poderia decidir se o método "salvar" deve instanciar a gravação em arquivos xml ou em bancos de dados, mas a visão continuaria pedindo apenas para "salvar" os dados, sem saber como isto seria feito.
Com essa tecnologia, podemos ter mais de um tipo de serviço (serviços para bancos de dados e serviços para arquivos xml, por exemplo) controlados por vários business delegates. Isso permitiria mudar a maneira como se faz a persistência dos dados sem modificar nada na camada de visão. Mas é preciso ainda mais um nível de desacoplamento que pode ser obtido implementando um facade (fachada), que será responsável por reduzir a complexidade dos BDs e fornecer uma classe com métodos que a camada de visão pode entender com maior facilidade. Por exemplo, com base em uma escolha do usuário ou por meio de um arquivo de configuração, o facade poderia decidir se o método "salvar" deve instanciar a gravação em arquivos xml ou em bancos de dados, mas a visão continuaria pedindo apenas para "salvar" os dados, sem saber como isto seria feito.
Vou mostrar a seguir o meu modelo de classe facade, que já é utilizado em vários projetos. No mesmo pacote delegate, criamos a classe facadeBD:
package delegate;
import entidades.Entidade;
import java.io.Serializable;
import java.util.List;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import org.primefaces.context.RequestContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("facadeBD")
@ManagedBean(name = "facadeBD")
@ViewScoped
public class FacadeBD implements Serializable {
public static final short SALVAR = 1;
public static final short SALVARLISTA = 2;
public static final short LISTAR = 3;
public static final short CARREGAR = 4;
public static final short CONSULTAPERSONALIZADA = 5;
public static final short CONSULTANATIVA = 6;
private Class classe;
private Serializable chavePrimaria;
private Entidade objetoRetorno;
private Entidade entidade;
private List listaRetorno;
private String consulta;
private List listaEntidades;
@Autowired
private GenericBD bd;
public FacadeBD() {
}
public void salvar(Entidade entidade) {
this.entidade = entidade;
executar(SALVAR);
}
public void salvarLista(List listaEntidades) {
this.listaEntidades = listaEntidades;
executar(SALVARLISTA);
}
public List listar(Class classe) {
this.classe = classe;
executar(LISTAR);
return listaRetorno;
}
public Entidade carregar(Class classe, Serializable chavePrimaria) {
this.classe = classe;
this.chavePrimaria = chavePrimaria;
executar(CARREGAR);
return objetoRetorno;
}
public List consultaPersonalizada(String consulta) {
this.consulta = consulta;
executar(CONSULTAPERSONALIZADA);
return listaRetorno;
}
public List consultaNativa(String consulta) {
this.consulta = consulta;
executar(CONSULTANATIVA);
return listaRetorno;
}
public Boolean mensagemNova(FacesMessage mensagem) {
Boolean adicionar = true;
List<FacesMessage> lm = FacesContext.getCurrentInstance().getMessageList();
if (lm != null && lm.size() > 0) {
for (FacesMessage fm : lm) {
if (fm.getDetail().trim().equals(mensagem.getDetail().trim())) {
adicionar = false;
}
}
}
return adicionar;
}
public void executar(short metodo) {
FacesMessage mens;
String mensagem = "";
try {
if (metodo == SALVAR) {
bd.salvar(entidade);
}
if (metodo == SALVARLISTA) {
bd.salvarLista(listaEntidades);
}
if (metodo == LISTAR) {
listaRetorno = bd.listar(classe);
}
if (metodo == CARREGAR) {
objetoRetorno = bd.carregar(classe, chavePrimaria);
}
if (metodo == CONSULTAPERSONALIZADA) {
listaRetorno = bd.consultaPersonalizada(consulta);
}
if (metodo == CONSULTANATIVA) {
listaRetorno = bd.consultaNativa(consulta);
}
} catch (Exception e) {
System.out.println("===== erro ====");
if (classe != null) {
System.out.println("Classe: " + classe.getSimpleName());
} else {
System.out.println("Classe: null");
}
System.out.println("Método: " + metodo);
System.out.println("Consulta: " + consulta);
System.out.println("Chave: " + chavePrimaria);
e.printStackTrace();
System.out.println("===============");
mens = new FacesMessage(FacesMessage.SEVERITY_FATAL, "Erro!", e.getMessage());
if (mensagemNova(mens)) {
FacesContext.getCurrentInstance().addMessage(null, mens);
}
RequestContext.getCurrentInstance().update("growl");
} finally {
if (!mensagem.isEmpty()) {
mens = new FacesMessage(FacesMessage.SEVERITY_INFO, "Sucesso", mensagem);
if (mensagemNova(mens)) {
FacesContext.getCurrentInstance().addMessage(null, mens);
}
RequestContext.getCurrentInstance().update("growl");
}
}
}
public GenericBD getBd() {
return bd;
}
public void setBd(GenericBD bd) {
this.bd = bd;
}
}
- Como podemos ver na anotação da linha 15, facadeBD é um managed bean, e poderá ser injetado por meio do JSF em outro managed bean com o mesmo escopo (@ViewScoped).
- facadeBD também é um componente no contexto do Spring, e pode receber uma injeção do GenericBD (linhas 32 e 33). Aqui cabe uma observação: para integrar o Spring com o JSF permitindo injeção de dependências entre os contextos, utilizamos uma pequena configuração no arquivo faces-config.xml:
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<application>
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
</faces-config>
- No trecho compreendido entre as linhas 38 e 71, temos a implementação dos métodos que serão utilizados pela camada de visão. Para salvar uma entidade que foi instanciada no objeto autor, por exemplo, a visão só saberá invocar "salvar(autor)". Todo o processamento de transações, conexão com o banco de dados, decisão sobre inserir ou atualizar, e outros detalhes, serão processados em suas respectivas camadas, de maneira transparente.
- O método executar que inicia na linha 86, desvia para o business delegate bd que foi injetado pelo Spring, todo o restante do processamento.
O restante do código resume-se aos gets e sets e processamento de mensagens do JSF.
No próximo post, a criação de um controlador do JSF que fará a integração entre as páginas e o facadeBD.
O método executar ficou no ar. Exemplos, exemplos e mais exemplos. Quando se deixam coisas "NO AR" se autodestre a bela intenção de ensinar. Lembre sempre que aquilo que para você parece "obvio" para a grande maioria não é.
ResponderExcluir