Camadas DAO e Service (JPA / Hibernate + Spring)

Estou criando um novo aplicativo baseado em JPA / Hibernate, Spring e Wicket. A distinção entre as camadas DAO e Service não é clara para mim. Segundo a Wikipedia, DAO é

um object que fornece uma interface abstrata para algum tipo de database ou mecanismo de persistência, fornecendo algumas operações específicas sem expor detalhes do database.

Eu queria saber se um DAO poderia conter methods que realmente não precisam fazer muito com o access a dados, mas são mais fáceis de serem executados usando uma consulta? Por exemplo, “obter uma lista de todas as companhias aéreas que operam em um determinado conjunto de aeroportos”? Parece-me mais um método de camada de serviço, mas não tenho certeza se o uso do JPA EntityManager na camada de serviço é um exemplo de boa prática?

Um DAO deve fornecer access a uma única fonte de dados relacionada e, dependendo de quão complicado seu modelo de negócios, retornará objects de negócios completos ou objects de dados simples. De qualquer forma, os methods DAO devem refletir o database com certa precisão.

Um Serviço pode fornecer uma interface de nível superior para processar não apenas seus objects de negócios, mas para obter access a eles em primeiro lugar. Se eu obtiver um object de negócios de um Serviço, esse object poderá ser criado a partir de bancos de dados diferentes (e de DAOs diferentes), ele poderá ser decorado com informações feitas a partir de uma solicitação HTTP. Pode ter certa lógica de negócios que converte vários objects de dados em um único object de negócios robusto.

Eu geralmente crio um DAO pensando que ele será usado por qualquer pessoa que use esse database ou dataset relacionados a negócios. Ele é literalmente o código de nível mais baixo além de gatilhos, funções e stored procedures no database.

Respostas para perguntas específicas:

Eu queria saber se um DAO poderia conter methods que realmente não precisam fazer muito com o access a dados, mas são mais fáceis de serem executados usando uma consulta?

para a maioria dos casos, não, você desejaria sua lógica de negócios mais complicada na sua camada de serviço, a assembly de dados de consultas separadas. No entanto, se você estiver preocupado com velocidade de processamento, uma camada de serviço pode delegar uma ação a um DAO mesmo quebrando a beleza do modelo, da mesma maneira que um programador C ++ pode escrever código assembler para acelerar determinadas ações.

Parece-me mais um método de camada de serviço, mas não tenho certeza se o uso do JPA EntityManager na camada de serviço é um exemplo de boa prática?

Se você for usar seu gerenciador de entidades em seu serviço, pense no gerenciador de entidades como seu DAO, porque é exatamente isso. Se você precisar remover alguma construção de consulta redundante, não o faça em sua class de serviço, extraia-a em uma class que utilizou o gerenciador de entidades e torne-a seu DAO. Se o seu caso de uso for realmente simples, você poderá ignorar completamente a camada de serviço e usar seu gerenciador de entidades, ou DAO nos controladores, porque tudo o que seu serviço fará é fazer chamadas para getAirplaneById() para getAirplaneById() do DAO

ATUALIZAÇÃO – Para esclarecer a discussão abaixo, usar um gerente de entidade em um serviço provavelmente não é a melhor decisão na maioria das situações em que também há uma camada do DAO por várias razões destacadas nos comentários. Mas na minha opinião, seria perfeitamente razoável, dado:

  1. O serviço precisa interagir com diferentes conjuntos de dados
  2. Pelo menos um dataset já tem um DAO
  3. A class de serviço reside em um módulo que requer alguma persistência, que é simples o suficiente para não garantir que o próprio DAO

exemplo.

 //some system that contains all our customers information class PersonDao { findPersonBySSN( long ssn ) } //some other system where we store pets class PetDao { findPetsByAreaCode() findCatByFullName() } //some web portal your building has this service class OurPortalPetLostAndFoundService { notifyOfLocalLostPets( Person p ) { Location l = ourPortalEntityManager.findSingle( PortalUser.class, p.getSSN() ) .getOptions().getLocation(); ... use other DAO's to get contact information and pets... } } 

Uma coisa é certa: se você usar o EntityManager na camada de serviço, não precisará de uma camada dao (apenas uma camada deve saber detalhes de implementação). Além disso, existem diferentes opiniões:

  • Alguns dizem que o EntityManager expõe toda a funcionalidade dao necessária, para que eles injetem EntityManager na camada de serviço.
  • Outros têm uma camada dao tradicional suportada por interfaces (portanto, a camada de serviço não está vinculada a detalhes de implementação).

A segunda abordagem é mais elegante quando se trata de separação de interesses e também facilita a mudança de uma tecnologia de persistência para outra (você só precisa reimplantar as interfaces dao com a nova tecnologia), mas se você sabe que nada vai mudar, o primeiro é mais fácil.

Eu diria que se você tiver um projeto pequeno, use o JPA na camada de serviço, mas em um projeto grande use uma camada DAO dedicada.

Este artigo de Adam Bien pode ser útil.

Tradicionalmente, você escreveria interfaces que definem o contrato entre sua camada de serviço e a camada de dados. Você então escreve implementações e estes são seus DAOs.

De volta ao seu exemplo. Supondo que o relacionamento entre o aeroporto e a companhia aérea é de muitos para muitos, com uma tabela contendo airport_id e airline_id, você pode ter uma interface;

 public interface AirportDAO { public List getAirlinesOperatingFrom(Set airports); } 

..e você pode fornecer uma implementação do Hibernate disso;

 public class HibernateAirportDAO implements AirportDAO { public List getAirlinesOperatingFrom(Set airports) { //implementation here using EntityManager. } } 

Você também pode procurar uma List na sua entidade Airline e definir o relacionamento com uma anotação @ManyToMany JPA. Isso eliminaria a necessidade de ter esse método DAO específico completamente.

Você também pode querer examinar o padrão Abstract Factory para criar fábricas DAO. Por exemplo;

 public abstract class DAOFactory { private static HibernateDAOFactory hdf = new HibernateDAOFactory(); public abstract AirportDAO getAirlineDAO(); public static DAOFactory getFactory() { //return a concrete implementation here, which implementation you //return might depend on some application configuration settings. } } public class HibernateDAOFactory extends DAOFactory { private static EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("myPersistenceUnit"); public static EntityManager getEM() { return emFactory.createEntityManager(); } public AirportDAO getAirportDAO() { return new HibernateAirportDAO(); } } 

Esse padrão permite que seu HibernateDAOFactory mantenha um único EMF e forneça instâncias individuais do DAO com EMs. Se você não quiser descer a rota fatorial, o Spring é ótimo para lidar com instâncias do DAO com a injeção de dependência.

Edit: esclareceu algumas suposições.

Dao é um object de access a dados. Ele armazena / atualiza / seleciona entidades no database. O object do gerenciador de entidades é usado para isso (pelo menos no jpa aberto). Você também pode executar consultas com este gerenciador de entidades. Não é sql, mas JPQL (Java persistence query language).

Exemplo simples:

 emf = Persistence.createEntityManagerFactory("localDB"); em = emf.createEntityManager(); Query q = em.createQuery("select u from Users as u where u.username = :username", Users.class); q.setParameter("username", username); List results = q.getResultList(); em.close(); emf.close();