Repositório, Dao e sua relação com a Entidade

23 respostas
hlegius

Salve galera !

Como alguns aqui já sabem (ou ao menos me viram no fórum nos últimos dias) estou tentando melhorar minha visão sobre OO para depois começar a me aprofundar mais em designer patters, porque senão vira uma sopa.

Então, por isto, li bastante ontem aqui no fórum e alguns blogs e sites - com destaque pro Fragmental do Phillip, o blog da Caelum e o Search do fórum.

Depois de muito ler, criei um modelo de aplicativo que simplesmente seta uns dados e "persiste-os" num possível database.
Tentei implementar: Repository, evitar os get/set burros, Dao (que não sei se seria bem esse o nome) além da Entidade. Abaixo o código:

Programa de teste
public class TestePessoa {

  public static void main(String[] args) {
    Pessoa maluco = new Pessoa("Fulano",12);
      maluco.adiciona(); //persistindo

      if(maluco.isMaior()) {
	    System.out.println(maluco.getNome() + " eh maior de idade");
      }else{
	    System.out.println(maluco.getNome() + " ainda eh menor de idade, coitado =~ ");
      }
  }
}

Nele eu usei o construtor da Entidade Pessoa para popular meu objeto com o nome e idade. Depois tento persisti-lo no banco de dados (que não existe) e entao pergunto ao objeto Pessoa se "ele" é maior de idade, ao invés do if(maluco.getIdade() >= 18 ) diretamente no programa teste.

A Interface

public interface PessoaRepository {
  public int salva(Pessoa maluco);
}

A Dao que a implementa...

public class PessoaDao implements PessoaRepository {

  public int salva(Pessoa maluco) {
    System.out.println(maluco.getNome() + " foi persistido no database");
    return 1; // seria o id do novo maluco, talvez =)
  }
}

e a entidade...

public class Pessoa {
  private String nome;
  private int idade;
  private PessoaRepository repositorio = null;

  public Pessoa() { }
  public Pessoa(String nome, int idade) {
    this.nome = nome;
    this.idade = idade;
  }

  public void adiciona() {
      this.setRepositorio(new PessoaDao()); // veja que eu to passando a PessoaDao como se fosse PessoaRepository
      this.repositorio.salva(this); // persiste...
  }

  public boolean isMaior() {
    if(this.idade >= 18) {
	return true;
    }else{
	return false;
    }
  }

  public void setRepositorio(PessoaRepository repo) {
      if(!(this.repositorio instanceof PessoaRepository)) {
	this.repositorio = repo;
      }
  }

  public String getNome() {
      return this.nome;
  }
}

Agora, gostaria de tirar algumas dúvidas, se for possível...

1. A partir do momento que a "DAO" implementa o "Repository" e eu na entidade Pessoa.setRepositorio exijo que o parametro seja a Interface, eu estou "codificando para uma interface ao invés de para uma implementação" como muitos dizem ser o mais correto ?

2. no Pessoa.adiciona, eu passo para o Pessoa.setRepositorio a DAO, uma vez que não posso passar a Interface, pois ela não é algo "concreto". Essa implementação estaria correta ?

3. Quanto aos get/setters: eu usei apenas o getNome() pois no programa eu precisaria exibir o nome dele, porém não criei o getIdade. Só que ao "persistir" lá na DAO eu precisaria ter *todos* os getters para passar ao Statement do DB para ele poder gravar, além de usar *boa parte* deles para a View. Isso não seria um pecado, ou seria ?

Acho que são "apenas" estes fantasmas que rondam minha cabeça por hora. Se alguém puder comentar algo também, será de grande valia!

Abraços!

23 Respostas

T

Cara dê uma lida nesse link.

http://www.javafree.org/content/view.jf?idContent=183

Abraço!

pcalcado

Oi,

Suas dúvidas não possuem muito a ver com Domain-Driven Design -lembre-se que DDD não é só usar meia dúzia de padrões ou nomes- então vou me ater ao uso de interfaces e não à semântica delas.

Sim. Você está trabalhando com o conceito e não com a implementação, o que geralmente é algo bom.

(dica: por que você precisaria do Repositório dentro da Pessoa?)

Seu objeto pessoa trabalha com o conceito Repositório e não com a implementação DAO. Isso é uma coisa boa mas instanciar a implementação dentro do objeto destrói este desacoplamento. A maneira comum de fazer isto em Java é ter uma terceira entidade que ou é perguntada quando você precisa de uma implementação ou injeta a implementação na sua classe. Nos dois modos sua classe lida apenas com a interface e nunca com a implementação.

Para saber mais procure sobre o padrão Registry e sobre Dependency Injection.

Se você estiver utilizando JDBC ou algum framework mais antigo (ou com uma abordagem diferente) você vai precisar de tais getters. A solução neste caso pode ser tanto ter os getters na classe (dependendo do caso não faz sentido complicar) ou fazer Pessoa ser uma interface que Não expõe o getIdade() mas que é implementada por uma classe que o faz. A solução ideal depende do caso.

Mas a melhor solução seria, certamente, utilizar uma abordagem moderna com Hibernate que não vai te obrigar a fazer estas coisas.

Dica: esqueça Domain-Driven Design por algum tempo e concentre-se em design Orientado a Objetos.

hlegius

truck1n,
Sua recomendação esclareceu bem a idéia ! DAO não é o fim, é um meio ao que pude entender. Com isso, após a DAO que é uma interface vem sua real implementação seja ela em JDBC, TXT, XML, enfim, a persistência “real”.

pcalcado,

pcalcado:
Sim. Você está trabalhando com o conceito e não com a implementação, o que geralmente é algo bom.

(dica: por que você precisaria do Repositório dentro da Pessoa?)

Legal. Menos um fantasma. Mas diga-me, essa “dica” significa que não haveria motivos “reais” para uma Pessoa implementar um Repositório ? Pelo que li à fora, Repository é útil quando você tem uma entidade, no caso Pessoa, persistindo informações em diferentes DAO’s uma vez que cada DAO tem a reponsabilidade de “cuidar” de um meio - database, xml, txt… - e no caso mais especifico de db, cada DAO cuidaria de “uma tabela”, correto ? Ou eu viajei demais nas leituras ? :lol:

Eu li, na verdade passei a manhã toda lendo sobre isto e pensando em possíveis implementações. A Dependency Injection (como abrevia isto ?) pode ser implementada de várias formas: construtor, interface ou injetando via setter. Hum ! Poderia eu também implementar esse “RepositoryImpl” usando uma fábrica, não ?

Só uma coisa não bateu ainda: esse “RepositoryImpl” seria para cada entidade da aplicação, logo, eu teria que ter outra interface para ele. Resumindo: ainda é uma “sopa” para mim. Busquei alguns exemplos, porém, tá complicado digerir essa ponte entre a camada de negócio e a camada de persistência… sequer consegui implementar alguma coisinha no meu “programinha exemplo” que postei lá no começo… alguma sugestão por onde começar ?

Sim. Estou tentando focar em partes para realmente entender as implementações e saber aplicar caso-a-caso, acho que com isso estou fugindo de aplicar padrões as cegas programas à fora, penso eu.

ah sim! Sobre os getters então não há pecado algum. Desde que eles não sejam inúteis, não haverá problema. (y)

Agradeço a força!

Abraços!

hlegius

Bom… depois de procurar mais infos, montei a seguinte idéia:

Objeto Pessoa tem injetado via setter um RepositorioImpl
RepositorioImpl Repositorio para que tal obedeça o contrato
O repositorioImpl contém as chamadas às Daos Necessárias para fazer a persistência do objeto, ou seja, se para persistir o Objeto Pessoa eu precisar de 2 tabelas distintas + umas configs no XML, eu chamaria 2 ou até 3 implementações de DAO’s para que cada uma delas execute sua tarefa própria que seria mapear o objeto para um sistema relacional (database) e uma outra DAO para persistir dados no XML.

Pelo que entendi de repositório seria isto. Ele abstrai diferentes DAO’s para que a entidade não saiba o que tem por trás daquele repositório.

Abraço!

pcalcado

Oi,

Por isso que eu disse: esqueça Domain-Driven Design por enquanto e se concetre em design orientado a objetos. Um repositório é algo bem diferente do que você mencionou mas acho que só é possível entender quando se entende a Camada de Negócios.

De qualquer modo, sobre seu exemplo em específico, não há necessidade da pessoa conhecer seu repositório, essa relação não faz parte do domínio e foi criada apenas para facilitar a vida do programador.

hlegius:
Eu li, na verdade passei a manhã toda lendo sobre isto e pensando em possíveis implementações. A Dependency Injection (como abrevia isto ?) pode ser implementada de várias formas: construtor, interface ou injetando via setter. Hum ! Poderia eu também implementar esse “RepositoryImpl” usando uma fábrica, não ?

Um container de dependências é como uma fábrica genérica de objetos.

Vai ser complicado mesmo no início. Você precisa entender melhor como Camadas são organizadas e como objetos são injetados. Como falei, acho que agora é melhor você esquecer Repositórios e começar pelo básico.

hlegius:
Bom… depois de procurar mais infos, montei a seguinte idéia:
Objeto Pessoa tem injetado via setter um RepositorioImpl
RepositorioImpl Repositorio para que tal obedeça o contrato
O repositorioImpl contém as chamadas às Daos Necessárias para fazer a persistência do objeto, ou seja, se para persistir o Objeto Pessoa eu precisar de 2 tabelas distintas + umas configs no XML, eu chamaria 2 ou até 3 implementações de DAO’s para que cada uma delas execute sua tarefa própria que seria mapear o objeto para um sistema relacional (database) e uma outra DAO para persistir dados no XML.

Pelo que entendi de repositório seria isto. Ele abstrai diferentes DAO’s para que a entidade não saiba o que tem por trás daquele repositório.

Não. Um repositório é apenas onde você guarda os objetos, ele não necessariamente abstrai nada. Apesar de mais uma vez recomendar que você esqueça repositórios, um Repositório vai estar ligado com um Aggregate Root e dependendo de como você armazena seus entities e value objects ele pode ou não abstrair seus DAOs.

Para começar, crie aplicações que possuem apenas uma Camada de Serviço usando DAOs para persistir objetos de negócio. Entender Domain-Driven Design sem experiência nisso é difícil porque quase toda a literatura foi criada para pessoas experientes.

hlegius

Okay! o “design orientado a objetos” que você diz é como a camada de negócios funciona ? Não entendi bem essa definição…

Sim. Depois que vi outros posts aqui no Guj onde você fala sobre a camada de negócios e sua relação com a persistência. Você falava que para a camada de negócios, uma vez em memória para sempre em memória, pois ela não “espera” que vá ser descarregada.

Então você recomenda que eu foque mais na camada de negocios e seu funcionamento, e por hora persista os dados diretamente na camada de negócios ?

Agradeço as dicas. Vou atrás disso com certeza!

Abraços!

sergiotaborda
hlegius:
A Interface
public interface PessoaRepository {
  public int salva(Pessoa maluco);
}

A Dao que a implementa...

public class PessoaDao implements PessoaRepository {

  public int salva(Pessoa maluco) {
    System.out.println(maluco.getNome() + " foi persistido no database");
    return 1; // seria o id do novo maluco, talvez =)
  }
}

Porque a interface é PessoaRepository e a implementação PessoaDAO ?
Se o DAO é o mesmo que um Repositorio use apenas uma das palavras.

public class PessoaDaoImpl implements PessoaDAO

ou

public class PessoaRepositoryImpl implements PessoaRepository

Por outro lado, qual é a responsabilidade de um DAO ?
E qual é a responsabilidade de um Reposiotorio ?
Posso ter um XMLDAO ? E um XMLRepository ?

PessoaDAO e PessoaRepository são o mesmo objeto ? Têm a mesma responsabilidade ? São baseados nos mesmos padrões ?

Pense nisso...

O DAO é um serviço de preservação de dados. Ele media a interação do sistema java com depósitos de dados através do uso de uma ou mais API especificas para esse tipo de deposito. O nome da implementação do DAO está ligado À tencologia que ele usa e/ou ao tipo de deposito que usa. Nomes como XMLDAO (arquivos XML), JDBCDAO (uso de JDBC) , LDAPDAO (usa LDAP) ou DataBaseDAO são válidos.

Vc teria um PessoaDAO e um JDBCPessoaDAO que é a implementação de PessoaDAO que usa JDBC para comunicar com o banco.
Vc pode ter ainda extenções de JDBCPessoaDAO para cada banco de dados. assim teriamos por exemplo OracleJDBCPessoaDAO
e SQLServer2005JDBCPessoaDAO.

O Repositório é um objeto permite aos objetos das entidades encontrarem outros objetos de entidade. Ele são modelados como se fossem coleções de dados com métodos especiais para procurar esses dados. Eles não são desenhados para serem um fachada para a preservação dos dados, mas na prática é util que sejam. Os repositórios funciona para uma certa entidade e são independentes da forma de preservação utilizada. Vc terá um PessoaRepository, mas nunca um XMLRepository porque isso significaria que o seu sistema tem uma entidade chamada XML (poderia, mas é incomum).

pcalcado

Não exatamente. Não apenas como esta camada funciona mas como objetos, em qualquer camada ou mesmo sem camadas, devem ser projetados. Talvez isso ajude a entender:

http://fragmental.tw/2008/09/23/object-oriented-design-which-how-and-what/

Sim mas isso não faz com que o objeto precise ter uma instância de seu repositório/DAO dentro dele. Procure ler sobre Service Layer e Façade.

Mais ou menos. Minha recomendação neste caso é que não procure entende Domain-Driven Design sem entender como criar uma aplicação Orientada a Objetos “normal” (i.e. que não usa diretamente os conceitos de DDD)

hlegius

sergiotaborda,

sergiotaborda:
Por outro lado, qual é a responsabilidade de um DAO ? E qual é a responsabilidade de um Reposiotorio ?

No que pude entender por nas leituras por aí, DAO seria a implementação da persistência, ou seja, o cara que salva em disco nosso objeto - ou como o Shoes disse num post: coloca-os para dormir X)

O repositório ao que entendi é uma interface dessas DAO's. Ele é único para a entidade (no exemplo Pessoa) e é ele que faz a comunicação com as DAO's existentes MySQLPessoaDAO, XMLPessoaDAO, etc.
Só que por exemplo: supondo que o objeto Pessoa ao ser persistido, necessite que: seja persistido o objeto no Database e que salve num XML, por exemplo, a hora em que ele foi persistido. São 2 rotinas: database e XML (exemplo beeem irreal, mas só pra constar mesmo). Neste caso surreal, o Repository iria servir "apenas" como uma façade para o XMLPessoaDao e JDBCPessoaDao ? Ou ele executa outra tarefa ? Veja, explico em código rapidinho:

class Pessoa { ...
//...

     public void salva() {
        try {
          this.id = RepositoryPessoaImpl.salva(this); // repositório com comportamento estático, acho que é uma saída...
        }catch(....
     }
//...
class RepositoryPessoaImpl implements RepositoryPessoa {
//...
     public static int salva(Pessoa maluco)  {
         // delegaria a JDBCPessoaDao para gravar no banco
        //  delegaria a XMLPessoaDao a tarefa de gravar o log no XML...
     }

Estaria essa idéia sobre o Repository correta, ou não tem nada a vê ? Realmente parece uma pattern simples mas digeri-la não está sendo fácil como as demais que tenho lido =~

sergiotaborda:
O Repositório é um objeto permite aos objetos das entidades encontrarem outros objetos de entidade. Ele são modelados como se fossem coleções de dados com métodos especiais para procurar esses dados.

Nesta sua explicação você diz que o Repositório permite uma entidade encontrar outra a qual tenha relação. Isso seria algo como: a entidade "Inventario" usa o RepositorioProdutos para localizar Produtos com certas especificações e retornar a coleção desses Produtos para ela ? OU ela usaria o RepositorioInventario e este conectaria-se às interfaces das DAO's dos Produtos para retornar um Produto[*] ?
Se for isto, a coleção seria "criada" no Repositório e devolvida já montada (num vetor, ou algo do tipo) pronto para o "Inventario", certo ? Assim eu teria que alterar somente o RepositorioProduto em caso de atualização na entidade Produto, penso.

pcalcado,

pcalcado:
Não apenas como esta camada funciona mas como objetos, em qualquer camada ou mesmo sem camadas, devem ser projetados.
É verdade. Eu ainda estou engatinhando nisto. Programar, programo faz um tempinho, mas aprofundar da forma correta em OO estou começando agora, por isso essa dificuldade em pegar as manhas.
pcalcado:
Sim mas isso não faz com que o objeto precise ter uma instância de seu repositório/DAO dentro dele. Procure ler sobre Service Layer e Façade.

Certo. Li a pattern façade e me pareceu bem simples. É uma interface que manipula outras interfaces com o objetivo de simplificar uma rotina. A grosso modo seria algo como eu ter 3 tarefas a seguir em passos, só que ao invés de chamá-las separado, posso "juntá-las" numa façade e rodar apenas um comportamento.

Assim, sendo a pattern repository aplicada como uma façade, eu teria apenas que chamá-la dentro da minha entidade usando o comportamento estático da minha Repository e ela que iria fazer o serviço sujo, apenas me retornando - ou não - algo no fim.

pcalcado:
Mais ou menos. Minha recomendação neste caso é que não procure entende Domain-Driven Design sem entender como criar uma aplicação Orientada a Objetos "normal" (i.e. que não usa diretamente os conceitos de DDD)

Tranquilo. Estou seguindo em partes como criar uma aplicação "OO pura" para ver e entender ela primeiro para depois entrar nas patterns mais complexas. Step by step X)

Agradeço muito a força !

Abraços!

pcalcado

Não. Minha sugestão é que você esqueça isso de colocar DAO (tentando sair dos nomes de DDD já que não estamos usando DDD) dentro do objeto e faça algo como isso:

class GerenciadorUsuarios{
 pubic void adicionaUsuario(String login, String senha, String nomeGrupo){
   Usuario u = new Usuario(login, senha);
   Grupo g = daoGrupos.buscaPorNome(nomeGrupo);
   g.adiciona(u);
   daoUsuario.salva(u);
   daoGrupo.salva(g);
 }
}

E este código é seu Façade, que é um Service Layer neste caso.

Note que com técnicas e tecnologias mais avançadas que JDBC básico você conseue eliminar algumas partes do exemplo acima.

hlegius

Caracas ! Ou esse pattern é muito simples e eu tô complicando ou talvez tenha que reconsiderar ir vender dog na esquina =/

aaah! Pera lá. Então esse Domain-Driven Design nada mais é do que essa sopa de nomes para patterns, principios e conceitos relacionados à OO ?

Entendi. Nesse caso seria o básico do básico. Um objeto que comunica com JDBC para persistir e que retorna um ArrayList com os valores pesquisados e lança umas Exceptions em caso de problemas.

Como eu posso diferenciar uma façade que também exerce função de Service Layer de uma façade que é apenas uma façade ?

Aproveitando que você citou a classe GerenciadorUsuarios: é sempre preferível eu ter um objeto que manipula as entidades à que elas mesmas “se auto-manipularem”, correto ? Ou seja, antes eu ter um GerenciadorUsuarios para CRUD à ter os comportamentos de persistência dentro da minha classe Usuario, por exemplo, certo ?

Abraços !

pcalcado

Acho que eh que voc^e est’a tentando aplicar uma solucao (pattern) sem entender o problema.

Este post, que eu ja linkei antes, tem uma visao sobre o assunto: http://fragmental.tw/2008/09/23/object-oriented-design-which-how-and-what/

nao sei se eu entendi esta frase.

Uma Facade pode ser como voce implementa uma service layer. Outra forma, por exemplo, eh atraves de Commands.

A classe ehe chamada GerenciadorXyz no exemplo porque eu nao conheco seu dominio. O importante no exemplo eh que o ’ gerenciador’ nao executa nenhuma regra de negocio, ele apenas executa o fluxo tira-objecto-do-banco->chama-metodo-de-negocio-no-objeto->coloca-no-banco-o-resultado-da-computacao.

hlegius

Opa!

Na verdade estou tentando entender a solução que o Repository podem vim a ajudar a resolver. Talvez por não conhecê-la direito eu esteja direcionando de forma errada minhas perguntas/afirmações sobre. Acho que é esse o problema =/ Talvez até não tenha sentido nenhum usar Repository para um objeto Pessoa (como coloquei no exemplo lá no começo), mas realmente estou tentando entender o básico dela: o que é, para que serve como posso aplicá-la.

Verdade, eu cheguei a ler mas por cima. Li agora com mais calma e pude entender mais sobre o DDD.

Talvez seja esse o motivo que esteja recomendando a mim que esqueça os DDD por hora, não ?

hlegius:
Entendi. Nesse caso seria o básico do básico. Um objeto que comunica com JDBC para persistir e que retorna um ArrayList com os valores pesquisados e lança umas Exceptions em caso de problemas.

Então, estava tentando dizer que o exemplo que você passou seria algo bem simples e sem preocupações com Repository, camada de persistência bem abstraída e etc.; Apenas um “corredor” entre as entidades e a camada de persistência.

Certo !

Shoes, obrigado mesmo pela ajuda e paciência ! :slight_smile:

Abraço!

tnaires

pcalcado:
Não. Minha sugestão é que você esqueça isso de colocar DAO (tentando sair dos nomes de DDD já que não estamos usando DDD) dentro do objeto e faça algo como isso:

class GerenciadorUsuarios{
 pubic void adicionaUsuario(String login, String senha, String nomeGrupo){
   Usuario u = new Usuario(login, senha);
   Grupo g = daoGrupos.buscaPorNome(nomeGrupo);
   g.adiciona(u);
   daoUsuario.salva(u);
   daoGrupo.salva(g);
 }
}

E este código é seu Façade, que é um Service Layer neste caso.


Phillip, tenho três dúvidas a respeito da sua sugestão:

  1. O método adicionaUsuario() recebe apenas dois parâmetros. Qual seria a forma ideal de proceder quando há 15, 20 parâmetros ou mais? Um usuário do GUJ me perguntou isso um tempo desses, e eu sugeri a ele duas coisas: utilizar um Map<String, String> com os nomes dos parâmetros e seus conteúdos, ou projetar uma DSL interna.

  2. Voltando a DDD só um pouquinho ( e sem querer focar exclusivamente nos padrões mas já focando :XD: ), ao invés de chamar o DAO eu poderia, por exemplo, injetar o repositório direto nesse Façade aí né? Ou o uso do repositório é exclusivo da camada de negócios?

  3. O Façade acima compõe a camada de aplicação certo? Seria ele, conceitualmente falando, o mesmo Service de que Eric Evans trata no livro dele?

Abraços.

pcalcado

Sobre estes exemplos espec’ificos leia o Patterns of Enterprise Applications Architecture, de Martin Fowler.

Por ai. Usando a terminologia do artigo, DDD eh uma decisao de 3o nivel mas voce ainda esta com dificuldade no segundo nivel, portanto eh melhor se concentrar nele.

Isso.

o restante deste post nao deveria estar em java basico

Dado que quem chama este facade eh a camada de apresentacao nao teria problema (falando de regra geral, depende do dominio) se esta criasse o objeto usuario e passase este para o Service layer. Acho que no seu exemplo voce teria que criar objetos baseados nos parametros (talvez usando uma Factory que pertence a Camada de Negocios) e passar estes objetos para os servicos.

Este exemplo eh completamente (e propositalmente) nao DDD. Num exemplo DDD voce nao pode ter um conceito de infra-estrutura como DAO sendo usado diretamente por uma classe de negocios. Neste caso -considerando que seu Repositorio seja uma implementado por um DAO JDBC- voce usaria Dependency inversion principle e sim, faria algo injetar o Repositorio.

Exato. lembre-se, entretanto, que Camada de Aplicacao geralmente eh diluida entre Apresentacao e Negocios.

Nao. Os Services do Evans possuem granularidade menor, o Service Layer do exemplo acima cuida de todo o fluxo de uma dada operacao.

Eh muito dificil dar um exemplo de Service sem ter um dominio em mente mas vamos pensar no seguinta: suponha que eu estaj num estacionamento de shopping. Quando o cliente entra de carro neste ele inforam em qual loja ir’a. O sistema guia ele para a vaga disponivel mais proxima daquela loja.

Em pseudo-Java:

class Estacionamento{
 public Vaga indicarVaga(Carro carro, Loja loja){
   VagaMaisProximaSpecification especificacao = new VagaMaisProximaSpecification(loja);
    Vaga vaga = localizadorDevaga.locaziarDeAcordoCom(especificacao);
    vaga.reservarPara(carro);
    return vaga;
 }
}

Note a diferenca entre Estacionamento#indicarvaga() e LocalizadorDeVagas#locaziarDeAcordoCom(). O primeiro 'e um Service Layer , ele executa o fluxo da aplicacao, nao regra de negocio em si. O segundo eh um Service Domain-Driven Design, ele executa regra de negocio.

hlegius

Opa, pode deixar ! Assim que eu finalizar os Head-first que tenho aqui irei providenciar uma cópia deste do Fowler (o qual é bem falando pelo fórum ao que pude perceber)

Certo !

Muito obrigado pelas dicas !

tnaires

OK Phillip, obrigado pelas respostas. Como sempre, muito esclarecedoras.

sergiotaborda
hlegius:
O repositório ao que entendi é uma interface dessas DAO's. Ele é único para a entidade (no exemplo Pessoa) e é ele que faz a comunicação com as DAO's existentes MySQLPessoaDAO, XMLPessoaDAO, etc.

Exato. Repare como a herança não está de acordo com o que vc mesmo disse.
1) O repositorio é único para cada entidade. Se existem várias implementações possiveis, então não é único. Logo, o repositorio não pode ser uma interface. Tem que ser um objeto concreto ( não abstrato, não interface, não estático)
2) O repositorio comunica com um ou mais DAO. Primeiro, herança não é comunicar. Comunicar significa uma relação de cooperação (de composição). Segundo, se o DAO for a implementação de repositorio então não tem como chamar mais do que um DAO.

Acho que isto já basta para mostrar que o Repositorio é um objeto distinto do DAO. Quando digo distintio quero dizer que um não RepositoryPessoaherda ou implementa o outro. A relação é Reposiotorio--->(1...*) DAO e não Repositorio---|> DAO nem DAO---|>Repositorio

Só que por exemplo: supondo que o objeto Pessoa ao ser persistido, necessite que: seja persistido o objeto no Database e que salve num XML, por exemplo, a hora em que ele foi persistido. São 2 rotinas: database e XML (exemplo beeem irreal, mas só pra constar mesmo). Neste caso surreal, o Repository iria servir "apenas" como uma façade para o XMLPessoaDao e JDBCPessoaDao ? Ou ele executa outra tarefa ?

Ele atua realmente como um façade para o DAO. Mais geralmente atual como façade para a camada inferior de preservação ( se existir). Repare que o repositorio gravar em dois daos já é uma coisa que justifica o façade (Que é o padrão para agrupar várias chamadas em uma so). Mas ele executa outas tarefas sim.
A tarefa principal de um repositorio é possibilitar que as instancias das entidades se encontrem e que os serviços encontrem as instâncias das entidades. Ou seja, ele é principalmente um "procurador" (locator). Por isso o respositorio tem um monte de métodos find. O ponto é que os métodos recebem os parametros da pesquisa e não a pesquisa em si.
O Repositório tem que criar um pesquisa no formato que o DAO entende (SQL para bancos, XPath para XML etc... )
No seu exemplo, o repositorio encontra as instancias onde ? no XML ou no banco ? ( ja que ele grava nos dois)
Essa decisão é algo que o Repositorio faz. Ele decide isso , cria a pesquisa, envia ao DAO no formato que ele tende e trabalha os dados de retorno. Um DAO tipicamente retorna algo semelhante a um resultSet ou a um Map. O Repositorio tem que instanciar a classe, preencher com os dados, verificar estados, etc... pode injetar objetos "filhos" etc...
Hoje em dia, um DomainStore é preferivel ao DAO extamente porque ele faz esse serviço chato de instanciação, preenchimento, etc.. Ou seja, ele assume o papel e o Repositorio simplesmente delega. Mas o Repositorio ainda tem que criar as pesquisas.
Na real, se usar o Hibernate que é uma implementação de um DomainStore, o Repositorio é onde vc cria os Criteria (ou HQL se quiser...) e envia ao Hibernate.

Veja, explico em código rapidinho:
class Pessoa { ...
//...

     public void salva() {
        try {
          this.id = RepositoryPessoaImpl.salva(this); // repositório com comportamento estático, acho que é uma saída...
        }catch(....
     }
//...
class RepositoryPessoaImpl implements RepositoryPessoa {
//...
     public static int salva(Pessoa maluco)  {
         // delegaria a JDBCPessoaDao para gravar no banco
        //  delegaria a XMLPessoaDao a tarefa de gravar o log no XML...
     }

Estaria essa idéia sobre o Repository correta, ou não tem nada a vê ?

É isso ai mas sem o static. O Repositorio é um objeto concreto. Mas tome atenção que o codigo salva() em pessoa não é necessário para o Repositorio.
Esse codigo em pessoa é o uso de um outro padrão (ActiveRecord). Vc pode salavar a pessoa simplesmente fazendo

RepositoryPessoaImpl  rep = new RepositoryPessoaImpl ();
rep.salva(pessoa);

Como a ideia é que o Repositorio funciona como uma coleção (ou seja, tem estado) vc não vai criar o respositorio cada vez que precisa dele. A sau solução foi usar static, mas isso viola o primeiro objetivo que é ter um objeto concreto e segundo, cria um "objeto global" que é sempre ruim. A solução é usar outro padrão o Registry. Este sim é um objeto com métodos só estáticos que funciona como um Map global com assinaturas especiais. Por exemplo:

RepositoryPessoa repPessoa = RepositoryRegistry.repositoryDePessoa();
rep.salva(pessoa);

ou simplesmente

RepositoryRegistry.repositorioDePessoa().salva(pessoa)

Várias variações são possiveis. Por exemplo:

Dominio.repositorioDe(Pessoa.class).salva(pessoa)

Vc pode argumentar que pode incluir este codigo em pessoa e inverter as coisas (pessoa.salva()). É verdade.
Mas eu não recomendo isso. É que com active record qualquer objeto pode mandar salvar a pessoa, mesmo quando não deveria.
O salvar é uma ação protegida por validações e outros processos do sistema que não cabem dentro do objeto pessoa ou do método salvar. Como conceito é legal, mas na prática é ruim se vc quer um sistema com fácil manutenção.( se vc não quiser, ignore o que eu disse...)

sergiotaborda:
O Repositório é um objeto permite aos objetos das entidades encontrarem outros objetos de entidade. Ele são modelados como se fossem coleções de dados com métodos especiais para procurar esses dados.

Nesta sua explicação você diz que o Repositório permite uma entidade encontrar outra a qual tenha relação. Isso seria algo como: a entidade "Inventario" usa o RepositorioProdutos para localizar Produtos com certas especificações e retornar a coleção desses Produtos para ela ?

Exatamente isso.

OU ela usaria o RepositorioInventario e este conectaria-se às interfaces das DAO's dos Produtos para retornar um Produto[*] ?

Definitivamente não.
Poderiamos ligar o RepositorioInventario ao RepositorioProdutos mas nunca ligar o RepositorioInventario ao DAOProdutos.

O repositorio se liga ao seu proprio DAO. Apenas a ele. Se precisar de entidades diferentes ele se liga ao Repositorio da outra entidade , não ao DAO dela. O DAO é visivel apenas pelo seu proprio Repositorio.

Veja que vc pode ter um mecanismo agnostico como o hibernate que tem a mesma invocação qualquer que seja a entidade, e desse ponto de vista o RepositorioInventário vè a mesma interface do Hibernate que o RepositorioProdutos. Mas isso não significa que deve haver quebre de encapsulamento. Isto porque se , por exemplo, o RepositorioCidade usa o RepositorioEstado que usa um EstadoDAOXML se o RepositorioCidade chamar directamente o EstadoDAOXML terei duas chamadas em lugares diferentes. Ao mudar para EstadoDAOJDBC terei que mudar em dois lugares. Mas se o RepositorioCidade chamar sempre o RepositorioEstado e apenas ele chamar o DAO, não terei problemas em mudar em um lugar apenas. Isto é válido mesmo quando o DAO é agnóstico (é o mesmo para qualquer entidade) ou quando se usa um DomainStore (como o Hibernate) que é agnóstico por construção.

Repito : o repositorio tem a responsabilidade de preservar os dados como e onde quiser. Ninguem pode atropelar esta responsabildiade. Outros objetos do sistema apenas podem invocar o repositorio e nunca os objetos que o repositorio usa. Isso seria uma violação grave do encapsulamento tornando o repositório de fato inutil.

Se for isto, a coleção seria "criada" no Repositório e devolvida já montada (num vetor, ou algo do tipo) pronto para o "Inventario", certo ? Assim eu teria que alterar somente o RepositorioProduto em caso de atualização na entidade Produto, penso.

Exatamente

Você entendeu. Agora faça o seu código corresponder às suas palavras. (porque não está... )

sergiotaborda
hlegius:
Bom... depois de procurar mais infos, montei a seguinte idéia:

Objeto Pessoa tem injetado via setter um RepositorioImpl
RepositorioImpl Repositorio para que tal obedeça o contrato
O repositorioImpl contém as chamadas às Daos Necessárias para fazer a persistência do objeto, ou seja, se para persistir o Objeto Pessoa eu precisar de 2 tabelas distintas + umas configs no XML, eu chamaria 2 ou até 3 implementações de DAO's para que cada uma delas execute sua tarefa própria que seria mapear o objeto para um sistema relacional (database) e uma outra DAO para persistir dados no XML.

Pelo que entendi de repositório seria isto. Ele abstrai diferentes DAO's para que a entidade não saiba o que tem por trás daquele repositório.

Sim. É isso mesmo.
O seu problema é que está pensando que "Cada entidade tem o seu repositorio" significa "Cada instancia da entidade tem uma instancia de um repositorio para a classe propria para a entidade". É isso que é falso.

Imagine um Repositorio assim

public class AbstractRepository <E> {

   public abstract List<E> findAll();
}

Este cara funciona para qualquer entidade E.
Agora pense numa implementação com Hibernate

public class HibernateRepository <E> extends  AbstractRepository <E> {

   public HibernateRepository (Class<E> type){
          this.type = type;
   }
   
   public List<E> findAll() {
         // cria criterio, executa no hibernate e retorna.
   } 
}

Esta implementação funciona para qualquer entidade E.

Agora tome atenção no Registro de reposiotorios.

public class RepositoryRegistry {

        private RepositoryRegistry (){}

        private static final Map<String, Repository> repositories; 
  
        public static <E> Repositoy<E> repositorioDe(Class<E> type){
                 
              Repositoy<E> rep = repositories.get(type.getName());

              if ( rep ==null ){
                     // cria um repositorio agnostico . poderia invocar um Factory
                     rep = new HibernateRepository (type); 
                     repositories.put(type.getName(),rep);
              }
              
              return rep;
       }

        
}

Agora vc pode fazer findALL para qualquer entidade do seu sistema e só teve que criar 3 classes.

Se vc entendeu o padrão Repositorio vc irá perguntar: mas e se a minha entidade tem métodos especiais no repositorio?
O registro é um registro porque coisas podem ser registradas nele ( :wink: ). Ou seja, no inicio do sistema vc pode
simplesmente registrar um repositorio especifico para um classe de entidade

public <E, R extends AbstractRepository<E> > void addRegistry(Classe<E> type, R repository){
     epositories.put(type.getName(),repository);
}

Depois pega da mesma forma e faz um cast para usar os métodos especiais ou simplesmente cria um método no registro que faça o cast por si.

Vc vai ter muito poucas classes e por isso um sistema mais conciso com manutenção mais fácil. Nada de 300 DAO

hlegius
sergiotaborda:
Exato. Repare como a herança não está de acordo com o que vc mesmo disse. 1) O repositorio é único para cada entidade. Se existem várias implementações possiveis, então não é único. Logo, o repositorio não pode ser uma interface. Tem que ser um objeto concreto ( não abstrato, não interface, não estático) 2) O repositorio comunica com um ou mais DAO. Primeiro, herança não é comunicar. Comunicar significa uma relação de cooperação (de composição). Segundo, se o DAO for a implementação de repositorio então não tem como chamar mais do que um DAO.

Acho que isto já basta para mostrar que o Repositorio é um objeto distinto do DAO. Quando digo distintio quero dizer que um não RepositoryPessoaherda ou implementa o outro. A relação é Reposiotorio--->(1...*) DAO e não Repositorio---|> DAO nem DAO---|>Repositorio

Bem, agora que você deu essa aula sobre Repository e DAO deixa eu monstrar um pequeno exemplo (agora mais real):

Entidade Pessoa:
public class Produto {
    private int id;
    private String nome;
    private int quantidade;

    public Produto(int id, String nome, int quantidade) {
	this.id = id;
	this.nome = nome;
	this.quantidade = quantidade;
    }

    public int getId() {
	return this.id;
    }
    public String getNome() {
	return this.nome;
    }

    public boolean checaQuantidade() {
	if(this.quantidade > 2) {
	    return true;
	}else{
	    return false;
	}
    }
}

Classe "de ação" Inventario

public class Inventario {
    private List produtos = new ArrayList();

    public void adiciona(Produto produto) {
	if(produto.checaQuantidade()) {
	    System.out.println("Produto adicionado. (" + produto.getNome() + ")");
	    this.produtos.add(produto);
	}
    }

    public boolean salva() {
	if(!(this.produtos.isEmpty())) {
	   RepositoryInventarioImpl repo = new RepositoryInventarioImpl();
	   repo.salvar(this.produtos);

	   return true;
	}else{
	    return false;
	}
    }
}

RepositoryInventarioImpl:

public class RepositoryInventarioImpl implements RepositoryInventario {

    public boolean salvar(List vetorProdutos) {
	RepositoryProdutoImpl repopdr = new RepositoryProdutoImpl();
	
	try {
	    boolean pdrstatus = repopdr.salvar(vetorProdutos);

	    return pdrstatus;
	}catch(Exception e) {
	    // lança exception
	}

	return false;
    }
}

RepositoryProdutoImpl:

public class RepositoryProdutoImpl {

    public boolean salvar(List vetorProdutos) {
	Iterator iterator = vetorProdutos.iterator();

	JDBCProdutoDao dao = new JDBCProdutoDao();
	try {
	    while(iterator.hasNext()) {
		Produto pdr = (Produto) iterator.next();
		dao.salvar(pdr); // manda a interação pro DAO...
	    }
	}catch(Exception e) {
	    // lança a exception (joga para frente)
	}

	return true;
    }
}

e a app teste...

public static void main(String[] args) {
	Produto pdr1 = new Produto(1, "Livro OOP", 1);
	Produto pdr2 = new Produto(2, "Livro Design Patterns", 3);
	Produto pdr3 = new Produto(3, "Abobora", 4);
	Produto pdr4 = new Produto(4, "Pringles :)", 5);

	Inventario estoque = new Inventario();
	estoque.adiciona(pdr1);
	estoque.adiciona(pdr2);
	estoque.adiciona(pdr3);
	estoque.adiciona(pdr4);

	if(estoque.salva()) {
	    System.out.println("Produtos Persistidos no database suavemente");
	}else{
	    System.out.println("'Faiou' a persistência...");
	}
    }

Bom. O que acontece é o seguinte:

1. Crio alguns objetos Produtos
2. "Inicializo" o Inventario
3. Adiciono os produtos 1 a 1 no Inventario
4. Chamo o estoque.salva() que irá persistir as informações no Inventario.

Agora vem o principal:
5. No Inventario#salva é chamado o RepositoryInventarioImpl#salvar() que recebe um List com os produtos do Inventario.
6. Esse Repository por sua vez, "delega" a ação de adicionar os produtos para outro Repository (como você sugeriu (explico abaixo o por quê fiz)), o RepositoryProdutoImpl.
7. RepositoryProdutoImpl, recebe esse List e como é tarefa dele, prepara os dados para serem repassados a DAO's necessária - neste caso somente 1 DAO, a JDBCProdutoDao.
8. JDBCProdutoDao se comunica com o database via jdbc e retorna um bool que é transmitido até chegar no Inventario#salvar(), ou, em caso de exceptions, dispara a Exception e essa se propagará até a Inventario#salvar() também.

Legal, agora os motivos disto (na minha opinião e entendimento à sua explicação):

RepositoryInventarioImpl existe, pois talvez eu possa ter alguma rotina de persistência na parte de inventário, logo, eu faço uma façade da RepositoryProdutoImpl para "juntar as rotinas". Façade simples, onde não impede a comunicação direta com o RepositoryProdutoImpl. (como pede o próprio pattern).

sergiotaborda:
A tarefa principal de um repositorio é possibilitar que as instancias das entidades se encontrem e que os serviços encontrem as instâncias das entidades. Ou seja, ele é principalmente um "procurador" (locator). Por isso o respositorio tem um monte de métodos find. O ponto é que os métodos recebem os parametros da pesquisa e não a pesquisa em si.

Isso quer dizer que caso eu venha a ter a necessidade de persistir alguma outra informação relacionada à entidade Produtos - como o usuário que fez a alteração no estoque, criando uma espécie de log - durante um ação de salvar(), eu poderia muito bem deixar a cargo do meu RepositoryInventarioImpl receber as novas entidades (via parametros no comportamento) e cuidar de persisti-las para mim, correto ?

sergiotaborda:
O Repositório tem que criar um pesquisa no formato que o DAO entende (SQL para bancos, XPath para XML etc... )
Certo. Nada de mau teria caso eu repassasse o objeto Pessoa para a DAO, pois até onde sei, é na DAO que eu crio o Statement, e passo os valores usando bindParam, por exemplo.
sergiotaborda:
Um DAO tipicamente retorna algo semelhante a um resultSet ou a um Map. O Repositorio tem que instanciar a classe, preencher com os dados, verificar estados, etc... pode injetar objetos "filhos" etc...
Jóia. DAO retorna o renomado ResultSet para o Repositório responsável, o qual se vira para criar os objetos de retorno.

Então o Repository basicamente garante que o objeto seja "traduzido" para o idioma do banco de dados/XML/TXT, e ao mesmo tempo, "traduz" tabelas e linhas em objetos que o sistema entende. Ele é nosso tradutor, nosso modem (analogia ruim, mas vale).

sergiotaborda:
Mas tome atenção que o codigo salva() em pessoa não é necessário para o Repositorio. Esse codigo em pessoa é o uso de um outro padrão (ActiveRecord).
Verdade. No ActiveRecord a implementação da persistência é feita diretamente na entidade. Já com o Repository aplicado, ele é quem cuida disto.
sergiotaborda:
Como a ideia é que o Repositorio funciona como uma coleção (ou seja, tem estado) vc não vai criar o respositorio cada vez que precisa dele. A sau solução foi usar static, mas isso viola o primeiro objetivo que é ter um objeto concreto e segundo, cria um "objeto global" que é sempre ruim. A solução é usar outro padrão o Registry. Este sim é um objeto com métodos só estáticos que funciona como um Map global com assinaturas especiais. Por exemplo:

huum! O Registry é o cara que "evita" que eu fique fazendo instâncias de Repositórios em todos comportamentos de persistência, como eu fiz no exemplo acima:

public boolean salva() {
	if(!(this.produtos.isEmpty())) {
	   RepositoryInventarioImpl repo = new RepositoryInventarioImpl();
	   repo.salvar(this.produtos);

	   return true;
	}else{
	    return false;
	}
    }

Ele me auxilia no "encapsulamento" dessas instâncias... seria meio que uma fábrica (factory) então ?

sergiotaborda:
É que com active record qualquer objeto pode mandar salvar a pessoa, mesmo quando não deveria. O salvar é uma ação protegida por validações e outros processos do sistema que não cabem dentro do objeto pessoa ou do método salvar.
Certo. No caso, o Inventario é uma classe que apenas possui comportamentos de persistência, logo, neste caso em especifico, não há problema em trabalharmos com o salvar() nele, correto ?

Falando nisto, veja:

public void adiciona(Produto produto) {
	if(produto.checaQuantidade()) {
	    System.out.println("Produto adicionado. (" + produto.getNome() + ")");
	    this.produtos.add(produto);
	}
    }

Antes de jogar no List mais um objeto Produto, o Inventario comunica-se com o objeto Produto que foi passado a ele para ter certeza de uma regra em espeficico. Caso, negativo nada é feito, do contrário, o produto entra na lista do Inventario.
A pergunta é: validações de regra de negócio é preferível ficar sempre no modelo, correto ? Pois uma possível implementação seria validar no RepositoryProdutoImpl, mas eu mesmo não achei muitos motivos para isto...

sergiotaborda:
Repito : o repositorio tem a responsabilidade de preservar os dados como e onde quiser. Ninguem pode atropelar esta responsabildiade. Outros objetos do sistema apenas podem invocar o repositorio e nunca os objetos que o repositorio usa. Isso seria uma violação grave do encapsulamento tornando o repositório de fato inutil.

Certo. Repare que no exemplo, eu mando um List de Produtos ao repositório para serem persistidos. Repare também que é o Repository quem faz a interação item a item e adiciona usando a DAO em jogo. Logo, no meu exemplo, eu não conseguiria "pular" o Repositório (algo como: Inventario -> ProdutoDAO), pois estou dependendo da interação. Por isto, estaria meu repositório executando tarefas que não é obrigação dele ? Ou seja, essa interação deveria ficar lá no Inventario#salvar(), ou o Repositório pode colocar a mão-na-massa também ?

Realmente você explicou de forma excelente ! Sem dúvidas foi bem útil para mim !

Abraços e muito obrigado ! (y)

sergiotaborda
hlegius:
Bem, agora que você deu essa aula sobre Repository e DAO deixa eu monstrar um pequeno exemplo (agora mais real):

Classe "de ação" Inventario

public class Inventario {
    private List produtos = new ArrayList();

    public void adiciona(Produto produto) {
	if(produto.checaQuantidade()) {
	    System.out.println("Produto adicionado. (" + produto.getNome() + ")");
	    this.produtos.add(produto);
	}
    }

    public boolean salva() {
	if(!(this.produtos.isEmpty())) {
	   RepositoryInventarioImpl repo = new RepositoryInventarioImpl();
	   repo.salvar(this.produtos);

	   return true;
	}else{
	    return false;
	}
    }
}

Repare como vc mesmo assume que esta classe não é uma entidade. é uma "acção".
Na realidade ela está mais para serviço do que para entidade. (repare que não tem estado, e portanto não tem identidade, logo não é uma entidade).

Isso leva à conclusão que Inventário não é uma entidade e sim um serviço. Como tal, o inventário não tem repostorio.

Só paa deixar claro que o inventário é um serviço chamarei a classe de InventarioService.

Outra pista de que Inventário não é uma entidade é que o repositorio de inventário salva produtos. Então ele é um repositorio de produtos. Então não é um repositorio de inventário. Este paradoxo mostra que tb que inventário não é uma entidade e não precisa de repositorio.

Por outro lado vc está usando Inventário como uma coleção de produtos como se pode ver no main.
Na realidade vc precisa de um objeto ajudante para ser essa coleção e o inventário trabalha com essa coleção

Chamemos este objeto ajudante de ProductBag.
Remova o repositorioinventario e rearranje o inventário assim:

public class ProductBag  implements Iterable<Produto>{
 private List produtos = new ArrayList();

     public ProductBag  adiciona(Produto produto) {
           this.produtos.add(produto);
           return this; // permite encadeamento de métodos para simplificar o uso
    }

    public Iterator<Produto> iterator(){
         return produtos.iterator();
   }

   public boolean isEmpty(){
 return pordutos.isEmpty();
   }

}

public class Inventario {
   

    public boolean salva(ProductBag bag) {

        if(bag.isEmpty())) {
              return false;
        }

        List<Produto> produtosASalvar = new LinkedList<Produto>();
         for ( Produto produto : bag){
             if(produto.checaQuantidade()) {
                 System.out.println("Produto adicionado. (" + produto.getNome() + ")");
                 produtosASalvar.add(produto);
             }

         }
	

        RepositoryProdutoImpl repopdr = new RepositoryProdutoImpl();
	return repopdr.salvar(produtosASalvar);

	}
    }

    }
}
public static void main(String[] args) {
	Produto pdr1 = new Produto(1, "Livro OOP", 1);
	Produto pdr2 = new Produto(2, "Livro Design Patterns", 3);
	Produto pdr3 = new Produto(3, "Abobora", 4);
	Produto pdr4 = new Produto(4, "Pringles :)", 5);

	ProductBag estoque = new ProductBag ()
	.adiciona(pdr1)
	.adiciona(pdr2)
	.adiciona(pdr3)
	.adiciona(pdr4);

        Inventario inventario = new Inventario();
        
	if(inventario .salva(estoque )) {
	    System.out.println("Produtos Persistidos no database suavemente");
	}else{
	    System.out.println("'Faiou' a persistência...");
	}
    }

O inventário atua controlando quais produtos do bag podem ser adicionado ou não. Isso é um exemplo conceptual de uma regra
de dominio/negocio.

sergiotaborda:
A tarefa principal de um repositorio é possibilitar que as instancias das entidades se encontrem e que os serviços encontrem as instâncias das entidades. Ou seja, ele é principalmente um "procurador" (locator). Por isso o respositorio tem um monte de métodos find. O ponto é que os métodos recebem os parametros da pesquisa e não a pesquisa em si.

Isso quer dizer que caso eu venha a ter a necessidade de persistir alguma outra informação relacionada à entidade Produtos - como o usuário que fez a alteração no estoque, criando uma espécie de log - durante um ação de salvar(), eu poderia muito bem deixar a cargo do meu RepositoryInventarioImpl receber as novas entidades (via parametros no comportamento) e cuidar de persisti-las para mim, correto ?

Poderia, mas seria bem mais facil criar uma outra entidade (auditoriaProduto) o serviço receber o usuário e invocar o repositorio de produto normalmente, e depois o repositorio de AuditoriaProduto com referencia ao produto , usuário, data e resto dos paramentros de auditoria. Estes "cutting concerns" - coias que temos que fazer que não pertencem ao dominio, mas ao sistema - são melhores de tratar em serviços. Aliás , o resto da sua aplicação só deve mexer com serviços quando vai alterar alguma coisa.
É por o que eles fazem tende a alterar-se com o tempo.

sergiotaborda:
O Repositório tem que criar um pesquisa no formato que o DAO entende (SQL para bancos, XPath para XML etc... )
Certo. Nada de mau teria caso eu repassasse o objeto Pessoa para a DAO, pois até onde sei, é na DAO que eu crio o Statement, e passo os valores usando bindParam, por exemplo.

Não teria nada de mal em principio. Ao passar o objeto ara o DAO e o DAO passar o objeto de volta, isso significa que o DAO está acoplado ao objeto. Isso provavelmente significa que cada entidade tem o seu proprio DAO. Isso funciona, é possivel de usar assim, é sobretudo util quando usa bancos legados. É um trade-off possivel em certas circustancias. Mas se vc está fazendo um sistema do zero, onde pode criar seu proprio modelo de dominio e de dados, o DAO não têm por quê ser acoplado. É melhor usar um DomainStore como o JPA ou o Hibernate ( ou fazer o seu próprio) .
Portanto, o "mal" e o "bem" dependem das cisrcunstancias. Das escolhas que vc tem que fazer, dos constrangimentos com que tem que lidar ( tempo, pessoal, tecnológico , legado, etc... )

Conceptualmente não tem nenhum problema a sua escolha... desde que seja tomada com consciência.

sergiotaborda:
Um DAO tipicamente retorna algo semelhante a um resultSet ou a um Map. O Repositorio tem que instanciar a classe, preencher com os dados, verificar estados, etc... pode injetar objetos "filhos" etc...
Jóia. DAO retorna o renomado ResultSet para o Repositório responsável, o qual se vira para criar os objetos de retorno.

Então o Repository basicamente garante que o objeto seja "traduzido" para o idioma do banco de dados/XML/TXT, e ao mesmo tempo, "traduz" tabelas e linhas em objetos que o sistema entende. Ele é nosso tradutor, nosso modem (analogia ruim, mas vale).

a realidade o modem é a camada de persistencia (DAO, ou DomainStore). Mas dependendo da implementação da camada de persistencia tlbv o repositorio tenha que fazer algumas tarefas como as que falei. O ideal é que ele não tenha que as fazer ( dai a importancia do DomainStore), mas caso sejam necessárias a responsabilidade é do repositorio. Básicamente, quando mais inteligente a sua camada de persistencia, menos trabalho o repositorio faz.

sergiotaborda:
Como a ideia é que o Repositorio funciona como uma coleção (ou seja, tem estado) vc não vai criar o respositorio cada vez que precisa dele. A sau solução foi usar static, mas isso viola o primeiro objetivo que é ter um objeto concreto e segundo, cria um "objeto global" que é sempre ruim. A solução é usar outro padrão o Registry. Este sim é um objeto com métodos só estáticos que funciona como um Map global com assinaturas especiais. Por exemplo:

huum! O Registry é o cara que "evita" que eu fique fazendo instâncias de Repositórios em todos comportamentos de persistência, como eu fiz no exemplo acima: (...)

Ele me auxilia no "encapsulamento" dessas instâncias... seria meio que uma fábrica (factory) então ?

Não. Fabrica é fábrica e registro é registro. Registro é tipo um livro. Vc escreve (normalmente uma vez) e depois le (várias vezes). Fabrica é um objeto que cria o objeto. O Registro pode usar uma fábrica e ser um pouco inteligente, contudo, são dois padrões diferentes. Note que no exemplo do HibernateRepository eu digo que poderia usar um Factory em vez de dar o new directamente. Fabricas são substitudos de "new". Registros são substitutos de Maps globais e variáveis static.

Mas sim, com o registro vc evita ter que saber qual é o repositorio certo. Vc configura isso no inicio do sistema ( ou deixa o registro se virar sozinho fazendo algumas convenções) e depois só chama pelos repositorios - sem saber a verdadeira implementação deles ! (encapsulamento : essa é a vantagem)

Outro detalhe final. Vc ainda faz seus repositorios implementarem um interface. Entenda que a interface e a implemnetação estão fortemente acopladas. tão fortemente que nunca existirão duas classes implementando a mesma interface. Logo, a interface é ínutil. Vc pode herdar de um repositorio genérico (padrão Layer Supertype) , mas não separar interface e implementação. O repositorio tem que ser concreto ! já vimos que não faz sentido não ser concreto. Lembre-se disso. Vai poupar muita codificação desnecessária.

hlegius

sergiotaborda:
Repare como vc mesmo assume que esta classe não é uma entidade. é uma “acção”.
Na realidade ela está mais para serviço do que para entidade. (repare que não tem estado, e portanto não tem identidade, logo não é uma entidade).

aaah sim! Repository só aplica-se as entidades do sistema. Camadas de serviços não possui Repository ou DAO pois elas apenas executam ações para alguma entidade.
Aproveitando, deixa eu lhe perguntar: neste caso tudo bem, pois o Repository numa ação de findAll() por exemplo, retornaria uma coleção de Produto[] para o Inventário. Mas vamos supor uma situação em que não haja uma classe de serviço para atrelar à entidade. Eu acho que ficaria estranho um objeto Pessoa receber de RepositoryPessoa uma coleção de Pessoa[], não acha ? Como eu poderia proceder nestes casos ?

sergiotaborda:
Por outro lado vc está usando Inventário como uma coleção de produtos como se pode ver no main.
Na realidade vc precisa de um objeto ajudante para ser essa coleção e o inventário trabalha com essa coleção

Chamemos este objeto ajudante de ProductBag.

Com isso você sugere que nas classes de serviço eu não deva “manipular” as entidades da forma como eu fiz (criando uma coleção de Produtos) ? Elas (classe de serviço) devem sempre executar as ações e repassar o resultado, nada além disso ?

Vejamos: neste caso teriamos uma nova entidade (AuditoriaProduto), o RepositórioAuditoriaProduto e estes seriam manipulados pela camada de serviço Inventario igualmente o produto, penso. Neste caso, o serviçoInventario iria persistir o objeto no sistema e na sequência iria fazer a auditoria usando o RepositorioAuditoriaProduto.

Algo como:
Inventario#salva() --> RepositorioProduto -> Dao -> database
Inventario#salva() --> RepositorioAuditoriaProduto -> Dao(da auditoria) -> XML file

Como neste exemplo a Auditoria é uma regra para toda persistência do Produto, poderia eu deixar a implementação da entidade AuditoriaProduto a cargo da entidade Produto, não ? Funcionaria meio que uma composição. Produto morre, auditoria morre também. Parece-me lógico, o que acha ?

Certo. Entendi !

Pois é. Estava reparando isso enquanto montava os testes aqui. Nesses casos a interface(interface mesmo) de nada serve/ajuda no Repositório. Obrigado pelo toque !

Abraço!

Eduardo_Amuri

A discussão estava interessante… :frowning:

Criado 5 de outubro de 2008
Ultima resposta 26 de jan. de 2009
Respostas 23
Participantes 6