Separation of Concerns (SoC) entre Services and Entities

[quote=sergiotaborda][quote=danielbussade]
Vamos supor um cadastro de Funcionario onde a Entity Funcionario esta definida assim:

@Entity
@Table(name="FUNCIONARIO")
public class Funcionario {
 
	public Funcionario() {
		this.dataPrimeiroAcesso=new Date();
		this.dataUltimoAcesso=new Date();
		this.ativo=true;
	}

//Getters e Setters omitidos

[/quote]

Por exemplo, aqui, como vc garante que a segunda data é igual à primeira ?
Deveria ser

	public Funcionario() {
		this.dataPrimeiroAcesso=new Date();
		this.dataUltimoAcesso=dataPrimeiroAcesso;
		this.ativo=true;
	}

A primeira regra de programação é : Conheça as API que usa.
Se vc investigar sobre hibernate vai descobrir existe um padrão chamado Session-in-View que era usado antigamente, e que básicamente controla a transação por sessão do usuário. Hoje, existem tecnicas mais evoluidas como o uso de ThreadLocal. Procura por HibernateUtils para ver como é isso.

O controle de transação é da aplicação: isto significa que não é do dominio. Logo, não é dos serviços ou repositorios.
A transação é controlada pelo aplicação, então ela tem que ser inicializada/terminada no MBean (explicitamente) ou em um proxy do Serviço (implicitamente). Não antes. Não depois.

bom, além disso um melhor controle de exceções ajudaria. Especialmente o encapsulamento do hibernate abaixo do DAO. Ou seja, ninguem sabe que o hiberante existe excepto o DAO. Isto também é SoC. Cada objeto deve ter apenas uma responsabilidade. Se todos conhecer o hibernate, session, etc… todos sabem demais.

O serviço ficaria então apenas

public class FuncionarioService {
	
	
	public void criarFuncionario(Funcionario funcionario)throws PersistenceException{
		FuncionarioDao dao = new DaoFactory().getFuncionarioDao();
		dao.save(funcionario);
		
	}
	
	public void atualizarFuncionario(Funcionario funcionario){
		FuncionarioDao dao = new DaoFactory().getFuncionarioDao();
		dao.update(funcionario);
	}

e o controle de transação, por exemplo, passaria para o MBean

[code]
public void atualizar(){

            Session session =  hibernateUtils.getSession();

           try {
                 session.beginTransaction();
	
	      funcionarioService.atualizarFuncionario(funcionario);
	       limpar();
	      preparePesquisar();	

		session.getTransaction().commit();
	} catch (HibernateException e) {
		session.getTransaction().rollback();
		throw new PersistenceException("Erro ao criar Funcionario",e); //ok
	}

}

[/code][/quote]

Então Sérgio eu uso o padrão OpenSession in View em conunto com HibernateUtils desta forma:

package br.com.oticaweb.util;

import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;

public class HibernateUtil {

	private static Logger logger=Logger.getLogger(HibernateUtil.class);

	private static SessionFactory sessionFactory;

	private static ThreadLocal<Session> sessions=new ThreadLocal<Session>();


	static{
		sessionFactory=new AnnotationConfiguration().configure().buildSessionFactory();
	}

	public static Session openSession(){
		if(sessions.get() !=null){
			logger.error("There was a session for this thread alredy");
		}
		sessions.set(sessionFactory.openSession());
		return sessions.get();
	}


	public static void closeCurrentSession(){
		sessions.get().close();
		sessions.set(null);
	}

	public static Session currentSession(){
		return sessions.get();
	}

}

E tenho um Filtro chamado HibernateSessionFilter

package br.com.oticaweb.util;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.log4j.Logger;

public class HibernateSessionFilter implements Filter {
	private Logger logger = Logger.getLogger(HibernateSessionFilter.class);

	public void destroy() {

	}

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain fc) throws IOException, ServletException {
		
		HibernateUtil.openSession();

		try {
			HibernateUtil.currentSession().beginTransaction();
			fc.doFilter(req, resp);
			HibernateUtil.currentSession().getTransaction().commit();
		} catch (Exception e) {
			try {
				if (HibernateUtil.currentSession().getTransaction().isActive()) {
					logger.debug("Trying to rollback database transaction after exception");
					HibernateUtil.currentSession().getTransaction().rollback();
				}
			} catch (Exception e1) {
				logger.error("Could not rollback transaction after exception!",e1);
			}

		} finally {
			HibernateUtil.closeCurrentSession();
		}

	}

	public void init(FilterConfig arg0) throws ServletException {

	}

}

O problema desta abordagem que eu estava utilizando e que no Meu ManagedBean nao tinha como ter certeza que o dado foi inserido no banco de dados, pois o Hibernate so efetuava a operacao no commit, q esta definida no Filtro, entao se eu mostrasse uma pagina antes tipo cadastrado com sucesso coreria o risco de estar dando uma mensagem “falsa”.

Agora se o controle de transacoes fica no MBean ai o Service neste caso perde o sentido Sergio, poderia chamar o Dao direto do MBean nao?

Obrigado mais uma vez!

Uma outra observação, o ManagedBean é da camada de Apresentação, não seria estranho a apresentação controlar a transação?

Quanto ao controle de exceções, neste ultimo caso onde vc colocou o controle de transacoes no MB, nem preciso relançar a exception somente formata-la e exibi-la para o Usuario.

[quote=danielbussade] Uma outra observação, o ManagedBean é da camada de Apresentação, não seria estranho a apresentação controlar a transação?
[/quote]

Penso o mesmo. Não seria melhor o ManagedBean chamar um Facade, que chama o serviço, e aí colocar a transação nesse facade?

Não. Essa responsabilidade não era do Service. Lembra ? Estava no service por motivos de implementação. Ao trazer isso para fora, nada mudou na responsabilidade do service e portanto ele ainda é necessário pela mesma razão que antes. Alterações têm que passar pelo service.

Não. A aplicação controla a transação. A aplicação é todo o codigo que não seja o de dominio. O MBEan não é do dominio, logo é da aplicação. Portanto, não é estranho. Digamos que o MBean é o ponto mais externo ( mais perto do usuario) que vc tem controle. portanto é nele que controles de fronteira devem acontecer. Transação é um. Controle de exceção é outro.

Como falei, vc poderia usar um Proxy do serviço para o tornar transacional. Essa seria a melhor solução embora seja tecnicamente um pouco mais complexa. Dessa forma fica claro que a responsabilidade é da aplicação, não do MBean e não do Service. Usar o MBen é melhor que o Serviço, usar proxy é melhor que MBean. É uma escala.

Só um adendo. Um Façade é um Serviço que chama mais do que um 1 outro serviço. Se vc criar uma classe para chamar o serviço e controlar a tranação isso não será um façade. Se essa classe tiver contrato diferente do serviço será um adapter, se tiver a mesma será um proxy.
Portanto, essa ideia de ter um cara no meio é implementada com o Proxy. Uma implementação do mesmo serviço que controla a transação e chama o serviço real. Dê uma olhada na classe java.lang.reflect.Proxy para ver como é simples (simples de criar, mais dificil de entender). Ou implemente um Decorator do seu serviço ( mais trabalhoso, mas mais simples de entender).