Informações domainstore / repository / dao

aqui no guj já li vários posts sobre esse assunto.
mas minha cabeça deu um nó! ainda mais sendo programador delphi.

enfim…

dúvida é a seguinte…
pelo que eu entendi repositorio monta consultas e o domainstore executa…
domainstore tmb vai persistir os dados no banco de dados…
como o domainstore vai saber informações do banco de dados e como vai saber como persistir esses dados ?!

citando comentário abaixo do site https://sergiotaborda.wordpress.com/desenvolvimento-de-software/java/do-dao-ao-domain-store/

Se vc não quiser suas classes annotadas ou usar XML vc realmente precisa de um DAO por entidade. Esse DAO seria responsável por fornececer os metadados que a tecnologia subjacente precisa (nem que seja apenas o nome e tipo de campo). O problema com isso é que é chato, demorado e pouco reutilizável. É melhor ter um mecanismo padrão que funcione com qualquer entidade e obter os metadados de uma forma padrão (algo como getMetadata(entidade.class) ) que pode ser implementada lendo de arquivo, anotações, ou implementada manualmente. Assim, não seria o DAO que vc tem que criar para cada entidade e sim o objeto que controi os metadados.

Lembre-se sempre que o DAO serve para abstrair a tencologia de acesso ao banco. Apenas isso. Nada mais do que isso. Contudo, em termos modernos, isso não é mais suficiente. Queremos controle de estado, cache, distribuição, critérios de procurar independentes de SQL , configurar pouco e fazer muito , etc… Tudo isto são coisas que apenas o DomainStore dá.

Felipe, tudo certo?

Cara, no livro Domain-Driven Design (meu livro de cabeceira, me julgue! :smile:), o Eric Evans ensina uma maneira bem interessante de abstrair o acesso aos dados, um pouco mais simples do que está sendo proposto.

Funciona assim: na tua camada de domínio, onde moram as entidades (como Usuário e Produto), objetos de valor (como Endereço em alguns casos, Cor, coisas do gênero, sem identidade), e o que ele chama de Serviços, que são provedores de funcionalidade que não cabem dentro de uma entidade e devem ser externalizadas.

O acesso às entidades é feito através de um outro conceito, também presente na camada de domínio: os Repositórios. Entenda um repositório como uma coleção que contem todas as instâncias de uma determinada entidade.

Por exemplo, se temos a entidade User no sistema, precisamos de um UserRepository, algo mais ou menos assim:

public interface UserRepository {
    User findById(long id);
    List<User> findAll();
    void save(User u);
    // uma série de outros métodos de busca, dependendo das suas necessidades
}

A camada de domínio (e as acima dela, como a camada de Aplicação) deve utilizar os repositórios através de suas interfaces. A implementação da interface deve ser injetada, sem amarra-la com o cliente.

No caso do JPA, já partindo para a camada de Infraestrutura, poderíamos fazer o seguinte:

public class JPAUserRepository implements UserRepository {
    @PersistenceContext
    private EntityManager entityManager;
    // implementação dos métodos da interface do repositório
    // utilizando JPA
}

No cliente do repositório, podemos fazer assim:

public class ApplicationFacade() {
    @Inject
    private UserRepository userRepository;
    @Inject
    private EmailService emailService;
    
    public void createUser(UserDTO userDTO) {
        User newUser = // pega os dados do userDTO (data transfer object) 
                       //e cria um novo usuario
        userRepository.save(newUser);
    }

    // exemplo de outros métodos que podem estar presente na application facade
    public void sendEmailToUser(UserDTO userDTO, String content) {
        User user = userRepository.find(userDTO.getId());
        emailService.send(content).to(user);
    }

}

No Java EE, é extremamente gostoso utilizar essa abordagem, porque podemos inicializar a transação em métodos na ApplicationFacade e propagá-la de forma automática (leia-se container managed) para os entity managers lá na camada de infraestrutura, pois o persistence context é amarrado à transação em andamento. Dessa forma, qualquer alteração que for feita em uma entidade buscada pelos entity managers nessa transação, mesmo na camada de aplicação, vão ser persistidas no banco de dados na hora do commit (se não der rollback por algum motivo). De forma resumida: você pode buscar uma entidade e alterá-la, sem se preocupar em ir explicitamente no repositório fazer repositorio.update(user). A mudança é propagada para você por causa da transação em andamento!

Dessa forma, podemos abstrair completamente a questão de performance do JPA, que pode ser configurada em outros lugares, como utilização e abordagem do cache em arquivos de configuração xml por exemplo.

E sobre a questão de não querer anotar as entidades nem criar arquivos de mapeamento xml em relação aos objetos do domínio:

Você pode utilizar apenas 1 classe User para tanto persistir, quanto para serializar para o cliente, como para fazer qualquer coisa, como você também pode utilizar uma versão da entidade para cada camada do software.

Por exemplo, você pode fazer com que a VIEW (pode ser um endpoint RESTful) receba os dados primitivos, como Strings e ints e converta-os para um objeto chamado de UserDTO. Com esse UserDTO, você pode chamar métodos na camada de aplicação, como updateUser(userDto). A camada de aplicação vai converter o DTO para uma entidade real do teu modelo de negócios e fazer os updates necessários. Nesse momento, antes de persistir a entidade, você pode converte-la para algum outro tipo mais específico para a camada de persistência.

Em caso de uma busca, por exemplo, funciona da mesma maneira: Parâmetros da busca chegam, são convertidos para a camada de aplicação, que as converte para a camada de domínio, faz a busca, entidades do domínio são retornadas, convertidas para DTOs e depois serializadas para JSON, XML ou qualquer outra coisa. Ufa!

Do mesmo jeito que você não precisa de um canhão para matar uma barata, também pode não ser necessário toda essa granularidade. Depende do caso!

Lucas Barbosa.

No exemplo que vc colocou, JPAUserRepository seria um DAO?!
Caso seja, pode uma camada de infra acessar uma camada de dominio, no caso o repositório?!
Caso não seja… precisarei fazer um curso de “reciclagem” urgente!!!

Como disse… sou programador delphi… e no delphi todas essas tecnologias são praticamente inexistentes…

Abs.

Felipe,

Me desculpe, eu não prestei atenção na parte que você falou que usava Delphi. Tens meu respeito! :grin:

Não seria bem um DAO. O DAO é implementado pelo JPA (Java Persistence Framework), é ele que abstrai todo o acesso e mapeamento das entidades para tabelas do banco de dados.

Breve explicação sobre JPA

Não sei se você já deu uma olhada no JPA. É uma especificação da Oracle (um conjunto de interfaces e anotações) que você pode utilizar para fazer o mapeamento objeto-relacional de classes Java para tabelas em um banco de dados relacional (que você deve manjar bastante já).

Inclusive, ele (JPA) possui uma linguagem de query muito parecida com SQL, chamada de JP QL (Java Persistence Query Language), que pode ser utilizada para fazer queries em relação aos objetos, e não às tabelas do DB. Essas queries são em formato de String, assim como no JDBC, o que faz com que elas tenham os mesmo problemas anteriores, como SQL Injection e problemas de queries dinâmicas. Para resolver isso, tem também um esquema bem interessante chamado de Criteria API, que te permite criar queries programáticas de uma forma bem intuitiva, sem ser em forma de strings, mas em forma de objetos que representam partes da query.

Como o JPA é só uma especificação, precisamos de alguém para implementá-la. É aqui que entram os Persistence Providers. Existem vários no mercado, mas os mais famosinhos são Hibernate, EclipseLink e OpenJPA. Eles implementam o que a especificação diz que tem que ser implementado, além de algumas funcionalidades extras (que você pode optar por utilizar, se amarrando mais àquele provider, ou não utilizar, ficando livre para trocar de um provider pra outro sem dor de cabeça).

Fim da explicação sobre JPA

Voltando. O DAO seria o objeto responsável por abstrair o acesso ao banco de dados, e, como eu falei, o JPA faz isso pra gente. Considero a classe JPAUserRepository como uma classe de infra, pois provê funcionalidades de “plumbing”. Confesso que agora, pensando bem, também acho essa interpretação meio estranha. A classe “depende” de uma interface da camada de domínio, o que seria uma violação de que uma camada deve entender apenas as que estão abaixo dela.

Talvez eu deva considerar o JPAUserRepository como uma classe de domínio mesmo, e o JPA como a infra. Enfim, não faz muita diferença!

Olhando o sistema de perfil, podemos ver algo assim:

View -> ApplicationFacade(aqui estão os limites das transações, begin e commit) -> Domínio (entidades, repositórios) -> Infra (email, DB, requisições web, etc)

(setinha = dependência)

Repositório é um conceito da camada de domínio. DAO é um conceito da camada de Infra. Um repositório pode utilizar um DAO para buscar/persistir dados, como pode também buscar/enviar dados para outro web service, ou algo do gênero.

Você não é obrigado a utilizar o JPA como eu falei anteriormente, pode implementar o DAO você mesmo e utilizá-lo. Dentro do DAO você vai implementar as consultas SQL e utilizar a API do JDBC para abrir conexões com o banco, ou mesmo implementar alguma outra classe responsável por manter um conection pool de onde você pega as conexões.

Aquela parte do DomainStore que você citou, acho que é extremamente desnecessário querer reinventar a roda, coisa de quem gosta de sofrer (ou de aprender hehe)! Se for um projeto novo, usa JPA que é muito mais produtivo e já tem tudo pronto.

O livro do Eric Evans é sensacional, recomendo demais a leitura. Tinha muita dúvida sobre essas questões e a grande maioria delas foi sanada.

Lucas,

Então eu entendi seu exemplo… ufa… valeu!!!

O que não entendo (por falta de prática e conhecimento) é o que vc disse…

JPAUserRepository é uma camada de infra e vc “viola as camadas”. Isso é correto ou aceitável?! vc tmb diz não fazer muita diferença. realmente não faz?!
Minha dúvida é justamente essa.
Posso ou não violar as camadas ?!
Se não posso, como faço para a camada de infra conhecer o banco de dados e conseguir fazer a persistencia?!

Não sei se fui claro… mas é que está tudo tão confuso que as vezes as perguntas tmb ficam confusas…

Abs

Aí que tá. Eu não acho que é uma camada de infra mais, pois a infra é realmente implementada pelo JPA, que é utilizado pela classe JPAUserRepository. Foi só uma interpretação errada minha, perdão. O JPA, que é infra, não conhece meu domínio, apenas meu domínio o conhece (no caso, o JPAUserRepository).

Aqui vale a lei de Gil. Não existe nenhum tipo de código sagrado te impedindo de violar esses limites entre as camadas. Há casos onde é extremamente mais simples violar e fazer funcionar do que passar dias pensando numa maneira bonita de fazer.

O JPA faz isso sozinho. Você diz qual o banco de dados que você quer utilizar, e o persistence provider vai se virar.

Caso você queira implementar o DAO manualmente, você vai fazer o seguinte. Cria uma interface pública chamada de UserDAO. Ela é da camada de infra. Classes específicas podem implementar essa interface, como: PostgresUserDAO, que tem toda a lógica para conectar com o postgres. Se você quiser trocar de DB, implementa outro UserDao pro DB específico e só troca qual é utilizada no teu código de domínio.

No repositório, você vai ter:

public class DaoUserRepository implements UserRepository {
    @Inject
    private UserDAO userDao; // repare que temos aqui a interface,
    //possibilitando a mudança de implementação do DAO de forma transparente.

    // Métodos que utilizam o userDao para fazer persistência e queries ...
}

Eu entendi a parte que a camada de infra depende da de domínio. O UserDao, por exemplo, depende do User da camada de domínio. Isso que você quis dizer?

Realmente, se a gente quiser abstrair isso e deixar a parte da infra totalmente independente, vai dar um trabalhinho. Por isso, repito, use JPA. Tá pronto e é fácil de configurar/sair usando! Os containeres de aplicação (o servidor onde você vai colocar tu aplicação java web) dão suporte nativo ao JPA, facilitando ainda mais a configuração e utilização. O negócio foi feito pra tirar esse trabalho da nossa mão!

Você estuda boas práticas de desenvolvimento lendo blog? e ainda escrito em 2008?

Eu buscaria uma literatura com mais substância, e mais recente. 9 anos é muito tempo em TI.

1 curtida

pfk66…
cada um estudo do jeito que quiser, ok?!
quando eu precisar saber algo sobre metodologias de estudo avançadas, eu entro em contato contigo…

2 curtidas