Factory para persistir classe adequadas ou existe outra partner para isso

58 respostas
J

Bom antes que alguem fale, sim ja pesquisei arduarmente sobre o assunto que irei introduzir, e o mais incrivel: ou existem topicos mto infantis com pessoas que nao sabem nada(desculpe nao estou querendo ser presuncioso) ou acham q sabem … ou assuntos avançandos que geralmente se disvirtuam.
vamos ao interessa.
por favor o exemplo que irei colocar é infantil porem, na maioria dos casos sempre ta errado. deem uma olhada no forum ou no google e vcs veram.

ipessoa
nome
sobrenome
TemFilhos

iEmpresa
razaosocial
TemFilial

Classe Pessoa implenta iPessoa
Nome
TemFilhos

iCliente
nome
DescontoDoCliente

ClientePessoa extents Pessoa e implementa iCliente
ClienteEmpresa extents Empresa e implementa iCliente

ja criei ClienteEmpresa e ClientePessoa pois o calculo de seus desconto sao diferentes, e logo adinte teram mais variações. Logo se a unica diferença fosse cpf e cnpj nao teria pq criar duas classes, porem na maioria dos exemplos a razao de ser ter duas classes para clientes é unica e exclusivamente cpf e cnpj q nao faz sentido nehum.

para mim nao me estender mto a questao é a seguinte

Classe OpVenda … essa classe tem um cliente que ira comprar alguma coisa, para essa classe oq importa é que quem vai comprar seja um cliente. Logo terei OpVenda.DefineCliente(iCliente cliente). e terei um OpVenda.DefineCliente.cliente_ID.

DefineCliente(iCliente cliente)
this.cliente = cliente
cliente_ID = cliente.ID

estarei passando como parametro no preenchimento da venda um ClientePessoa por exmplo.

Agora como faço quanto quero pesistir OpVenda?
como terei
apenas cliente_ID e uma variavel apontando para um interface iCliente
como irei carregar(pesistir) cliente se eu nao sei mais se ele é ClientePessoa ou CLienteEmpresa? e nao posso persistir uma interface
logo achei essa alternativa:

OpVenda.Carrear
iCliente this.cliente = new FactoryCliente.carregaCliente(thies.cliente_ID); //esse metodo retorna uma interface iCliente
//sendo que cliente_id ja esta persistido

FactoryCliente.carregaCliente(int cliente_ID) : iCliente
ClienteEmpresa ce = new ClienteEmpresa
if ce.carregar(cliente_ID) then
return := ce
else
ClientePessoa cp = new ClienteEmpresa
if cp.carregar(cliente_ID) then
return := cp

nao estou usando hibernate pois apenas quero endender se isso faz sentido.

Agradeço desde ja. desculpe pelos erros portugues, e desculpe a má implemntacao da sintaxe em java.

58 Respostas

R

Olá, achei seu modelo bastante complicado. Da uma ollhada nele.
So uma sugestão.

class Pessoa {}
class Venda{
List<Produto> produtos;
ClientePessoaFisica
ClienteEmpresa
}

Não entendi bem o seu modelo, da uma olhada quando fica meio confuso e que pode ficar melhor.

Bons Códigos.

S

Factory (Fábrica) é um padrão para criação de objetos. As fábricas constroem coisas.
Construir um objeto é fazer “new” dele. A fábrica mais simples é a que simplesmente faz new e retorna.
O que vc quer é um mecanismo que procure um objeto através de uma condição , no caso o valor do ID, e retorne o objeto dentro dessa condição.

Podemos imaginar , abstratamente, que todos os objetos de uma certa classe ficam numa lista na memoria.
Procurar por um deles significaria iterar esta lista e comparar os atributos dos objetos até que a condição de procura fosse satisfeita. O padrão que conrresponde a isto é o padrão Repository.

O ID é uma variável privada do sistema de persistencia. Se não existice banco de dados , vc não precisava dessa ID. ID é portanto uma chave para o sistema de persistencia de forma que o registro seja identificado de forma independente dos valores que ele contém. O que eu estou dizendo com isto é que vc não deve manipular o ID nem passar o ID de forma explicita. Se isso for necessário encapsule o ID num objeto.

A sua interface iCliente não tem um campo ID. Isso é correto. Mas o objeto ClientePessoa tem um ID. Porquê ? Porque ClientePessoa é um objeto persistivel.

Então vc pode fazer assim

interface Persistable 
getID() : Object 
setID(Object obj);

class ClientePessoas extends Pessoa implements Cliente , Persistable


class ClienteRepository {
  static Cliente findByID(Object id ){
    // código de procura

  }
}

Vc pode tirar o i de iCliente , em Java os nomes das interfaces não começam com I.
A longo prazo isso so serve para atrapalhar. Chama Cliente mesmo.

O OPVenda tem como parametro um Cliente. Eu não tendi muito bem o que seira OpVenda.DefineCliente(Cliente c)

parece uma método estático. Se for, tudo bem, mas chame algo mais perto da realidade OpVenda.vendePara(Cliente c)

Concerteza terá mais parametos como o produto e a quantidade ou vc seta isso depois com um addItem(Produto, quantidade)

Então se vc quer vender parao Cliente com id 345 ficaria assim

Cliente cliente = ClienteRepository.findByID(345); // procura o cliente
OpVenda venda = OpVenda.vendePara(cliente); // cria a venda
VendasRepositroy.store(venda); // persiste a venda

Repare que não precisa saber de 345 é uma pessoa ou empresa
o método findBy é que tem que descobrir isso e devolver o objeto certo.

K

sergiotaborda:
Factory (Fábrica) é um padrão para criação de objetos. As fábricas constroem coisas.
Construir um objeto é fazer “new” dele. A fábrica mais simples é a que simplesmente faz new e retorna.
O que vc quer é um mecanismo que procure um objeto através de uma condição , no caso o valor do ID, e retorne o objeto dentro dessa condição.

Podemos imaginar , abstratamente, que todos os objetos de uma certa classe ficam numa lista na memoria.
Procurar por um deles significaria iterar esta lista e comparar os atributos dos objetos até que a condição de procura fosse satisfeita. O padrão que conrresponde a isto é o padrão Repository.

O ID é uma variável privada do sistema de persistencia. Se não existice banco de dados , vc não precisava dessa ID. ID é portanto uma chave para o sistema de persistencia de forma que o registro seja identificado de forma independente dos valores que ele contém. O que eu estou dizendo com isto é que vc não deve manipular o ID nem passar o ID de forma explicita. Se isso for necessário encapsule o ID num objeto.

A sua interface iCliente não tem um campo ID. Isso é correto. Mas o objeto ClientePessoa tem um ID. Porquê ? Porque ClientePessoa é um objeto persistivel.

Então vc pode fazer assim

interface Persistable 
getID() : Object 
setID(Object obj);

class ClientePessoas extends Pessoa implements Cliente , Persistable


class ClienteRepository {
  static Cliente findByID(Object id ){
    // código de procura

  }
}

Vc pode tirar o i de iCliente , em Java os nomes das interfaces não começam com I.
A longo prazo isso so serve para atrapalhar. Chama Cliente mesmo.

O OPVenda tem como parametro um Cliente. Eu não tendi muito bem o que seira OpVenda.DefineCliente(Cliente c)

parece uma método estático. Se for, tudo bem, mas chame algo mais perto da realidade OpVenda.vendePara(Cliente c)

Concerteza terá mais parametos como o produto e a quantidade ou vc seta isso depois com um addItem(Produto, quantidade)

Então se vc quer vender parao Cliente com id 345 ficaria assim

Cliente cliente = ClienteRepository.findByID(345); // procura o cliente
OpVenda venda = OpVenda.vendePara(cliente); // cria a venda
VendasRepositroy.store(venda); // persiste a venda

Repare que não precisa saber de 345 é uma pessoa ou empresa
o método findBy é que tem que descobrir isso e devolver o objeto certo.

Boa sérgio, mandou bem … utilizando repository e tudo mais, não tem nem o que acrescentar :slight_smile:

J

Antes d td Obrigado Sergio pelas observações. É interessante citar que sou programar delphi, porem estou migrando algumas aplicações para java, e aproveitando para aplicar um poo descente e com bons beneficios.
bom … mais um comentario: se vc esta iniciando no mundo poo aproveitem esse otimo forum e as pessoas que participam dele. estou esperando a mais de 3 semanas uma resposta em um forum delphi e outro c#, ou respondem bobagens ou nao respondem nada.
A outra dica: peguem uma aplicaçao que ja esta funcionando e pronta e tentem migrar para uma programação oo. assim fica mais facil de entender os beneficios do poo, pois a regras e fluxos do negocio ja estao em sua cabeça.

bom vamos lá
Sergio vou abusar da sua boa vontade, ou daqueles que queiram contribuir para um assunto bem mascado porem ainda nao digerido por mtos.

quanto sua obs sobre o Persistable, valeu, mas como com certeza absoluta vou sempre usar um bd, vou manter o ID em classes que teram seus obetos persistidos.

class ClientePessoas extendsPessoa implementsCliente
quanto a isso entao esta tranquilo, fica a interface Cliente e a classe Pessoas

para Vendedor
class VendedorPessoas extendsPessoa implementsFuncionario

oq interessa para mim é seu findByID do repository

classClienteRepository {

staticCliente findByID(Object id ){

// código de procura

}
}

esse codigo de procura vai procurar primeiro em uma um obj da classe ClientePessoa se nao achar vai procurar em um obj da classe ClienteEmpresa? exemplo: ClientePessoaRepository.findByID depois ClienteEmpresaRepository.findByID isso dentro do repo do ClienteRepository.findBy
Pergunto isso pq Obj do tipo Cliente(interface) nao podem ser persistidos, e tb nao tenho um mapeamento com meu hibernate(por exemplo) de Cliente.
Cliente conceituamente nao existe, existe somente um ClientePessoa. sendo assim tenho mapeado ClientePessoa e Clienteempresa e esses sim eu posso usar resursos do meu framework de persistencia para buscar um ou outro atraves do ID.

quando eu digo que nao tenho um Cliente, é pq nao posso cadastrar ou conceber um Cliente(interface). eu sempre terei um ClientePessoa ou um ClienteEmpresa. estou dizendo isso pq ja vi implementações que tem a Classe Cliente, depois a classe ClientePF e ClientePJ que herdam de Cliente (“Classico isso”), porem se acredita que isso sao 3 modelos, 3 regras, e nao, pois qual a diferença de Cliente para ClientePF dentro do negocio.

no meu caso aqui quero apenas usar o recurso da interface Cliente para implementar no futuro ClienteEmpresaRevenda.

Se nao fui bem claro desculpa
Valeu gente

S

Desculpe, Joaquimnabuco, mas eu não tinha visto esta resposta.
Passo a comentar.

Não. Não é assim que funciona. Embora Cliente não seja algo concreto , existem realmente clientes no seu modelo.O que o seu modelo trata é de clientes , são eles que fazem compras. Não interessa se são pessoas ou empresas. O repositório deve apenas trabalhar com clientes e retornar apenas clientes. Vc escolheu usar uma interface para cliente, mas no seu caso uma classe abstrata seria melhor. Isto porque vc não vai criar uma classe que é simultanemente um Cliente e um não-cliente como ClienteFornecedor. O ponto, é que cliente é uma entidade abstracta do seu modelo. Ela é realmente mais importante que ClientePessoa ou ClienteEmpresa.

Os modelos de banco para isto são vários, mas em todos eles existe algum atributo da entidade abstrata que
identifica que tipo de subclasse está em causa. O caso mais simples é colocar todos os dados do cliente pessoa e do cliente empresa numa mesma tabela. Alguns campos vão ser comuns, como nome, por exemplo. Um campo q tb vai ser comum é o campo identiifcador de subclasse. Imagine-se que 1 é pessoa e 2 é empresa.
Então quando eu pesquisar o banco dentro do repositorio eu farei algo como “select * from Cliente where cliente_id = 357”, isto vai retornar todos os campos, entre os quais o campo que indeitifica a subclasse.
Ai eu faço o seguinte:

class ClienteRepository {   
  static Cliente findByID(Object id ){   

 // tou usando o JDBC para ser mais explicito
// repare que todos os objetos que saem daqui são Persistable.

     ResultSet rs = /// executa a query que filtra pelo ID, isto vai trazer 1 ou zero resultados
  if (    rs.next()){
     final int descriminador = rs.getInt("cliente_descriminador");
     if (descriminador ==1){
         // é um cliente pessoa 
         ClientePessoa pessoa = new ClientePessoa(id);
          // preenche os campos com os dados do resultset 
         
         return pessoa; // posso fazer isto porque pessoa é um cliente
     } else {
         // é um cliente empresa
         ClienteEmpresa empresa= new ClienteEmpresa (id);
          // preenche os campos com os dados do resultset 
         
         return empresa; // posso fazer isto porque empresa é um cliente

      }
     
} else {
   throw new Exception("Cliente não encontrado")
}
  }   

  static void store (Cliente cliente){
   //grava o cliente no banco

   // o problema é identificar o ID do cliente sem que cliente tenha a interface Persistable.
   // bom, o cliente não tem Persistable, mas todos os clientes reais têm. 
// Logo, eu posso sempre fazer isto:

   Persistable p = (Persistable)cliente;

// porque eu garanto no método acima que ClienteEmpresa e ClientePessoas são Persistable
    
// depois é  ler os campos e jogar num insert ou update
 }
}

Não. Cliente existe sim. É abstrato, mas existe. (se não existisse não haveria uma interface para o representar)

Não necessariamente. Se Cliente é uma classe abstrata vc nunca vai poder criar objecos que são apenas clientes sem serem pessoas ou empresas, mas poderá trabalhar com todos estes de forma unificada já que todos são clientes.

J

Po Sergio vc é rápido. Qualquer coisa me manda a conta pela a ajuda. Valeu mesmo.

Bom finalmente acho q vou resolver essas duvidas, pq nem com o prof da faculdade consegui resolver, por increvel que pareça. Bom comentários a parte vamos la:

Acho que talvez minha modelagem não esteja adequada. Será que não seria mais sensato eu fazer assim segundo oq vc comentou:

Interface Cliente
String Nome;
Double MeuDesconto …

Abstract class AbstractCliente implements Cliente

class ClientePessoa extends AbstractCliente implements Pessoa, Persistable

Sinceramente nunca vi uma modelagem coerente de clientes pessoa e empresas, acho q tem mta confusao por ai, embora pareça simples.

Eu estava usando uma interface Cliente anteriormente pois queria aproveitar a classe Pessoa e classe Empresa via herança(esse é um dos poucos lugares que usa herança pois sei os ricos disso). Como mais adiante teria o papel do vendedor eu tb usaria essa herança de pessoa, so que ao invés de implementar Cliente eu iria implementar Funcionario: class Vendedor extends Pessoa implements Funcionario, Persistable
Isso poupa o tempo de ter q fazer todos membros de pessoa e empresa, como os famigerados cpf e cnpj, que na verdade tem um porem, se a pessoa for americana ela nao vai ter isso, ou se a empresa for alemã tb não vai ter cnpj, mas o pior é ter que fazer a parte de endereço e telefone.

Voltando ao caso de Cliente:
no futuro vou ter ClienteEmpresaRevenda, ClienteFinal e outros mais, por isso achei o uso da interface Cliente mais adequada ao invés de usar uma classe abstrata implementando a propria Interface Cliente. Contudo se em um momento louco eu quiser vender para um cachorro, ou querer ter um cachorro como Cliente poderei class ClienteCachorro extends Cachorro implements Cliente , Persistable. e assim o metodo VenderPara(Cliente cliente) satisfaz a situação medonha.

Bom aqui vejo que seria interessante abrir um tópico somente para essa discussão de Pessoa e Empresa loco que se procurarmos na net veremos que os exemplos sao grotescos, salve o artigo que tem nesse site sobre interface.

Contudo agradeço mais uma vez Sergio e aos demais que postaram aqui.
Abraços

S

Modelar estruturas de negocio não é fácil. No fim vc vai depender de implementar uma certa estrutura que tena na cabeça , testar e remodelar. Esse processo iterativo e ciclico acaba sendo a melhor opção.
Por outro lado, vc precisa definir os objetivos dos seus modelo ou ele será gigante.

Começar por uma interface é muito bom. Eu sempre prefiro isso. Afinal Cliente é um papel que uma pessoa desempenha (é uma “interface” no sentido que ele se apresenta de certa forma). Usar classes Abstratas que implementam os métodos mais comuns da interface tb é uma mao-na-roda.

Se vc está pensando em ter clientes estrangeiros tlv o seu modelo de Pessoa e Empresa tenha que ser bem mais genérico e tlv um campo CNPJ não faz sentido nesses casos. Tlv um campo Pais ajude a destinguir e junto com o descriminador que falei antes ajude a criar vários tipos de clientes como ClienteEmpresaNacional (que tem o campo CNPJ) e ClienteEmpresaEstrageira ( que não tem CNPJ, mas pode ter outro identiifcador) o mesmo para pessoa.
Ou tlv vc queira um campo chamado identificadorFiscal que todos os clientes têm que é um objeto IDFiscal que pode ter implementações diversas: CPF extends IDFiscal ; CNPJ extends IDFiscal ; IDInternacional extends IDFIscal. Com isto vc poupa de criar tantos tipos diferentes de clientes e concentra-se nas diferenças reais dos seus atributos.

Enfim, não é simples, e no fim é uma questão de gosto…

Y

Esse exemplo do clienteFisica e clienteJuridica da mto pano pra manga mesmo, e ninguem tem a solucao milagrosa pro caso.

O Sergio deu otimas respostas na minha opiniao.

A minha solucao (depois de muita cabecada tbm) foi mais ou menos assim:

se vc tem pessoa fisica e juridica com muitas difierencas elas nao devem implementar a mesma interface soh por se chamarem pessoa(AlgumaCoisa), se elas sao parecidas eu usei o pattern state. O cliente possui uma referencia ao objeto TipoPessoa(ou qqr nome melhor q esse q lhe vier a cabeca - nao achei nenhum ainda) q é responsavel por manter os atributos q diferenciam as pessoas

ex:

public class TipoPessoa{

private String indicadorFiscal;

private bool isValido(){
     //verifica se é valido
}

//get,set

}

public class PessoaFisicao extends Tipopessoa{
}

public class Pessoa{

private Endereco endereco;

private TipoPessoa tipo;

}

O q eu fiz eh um pouco mais complexo, com classes CPF e CNPJ, extendendo Identificador e sendo usada como membros de TipoPessoa.

Acho q ainda esta longe de ser a solucao ideal, mas eh a melhor q eu tenho ateh agora.

F

Sérgio,

No seu exemplo acima, nao estou vendo muita diferença entre seu Repository e um DAO.

Se eu falei besteira, poderia me explicar a diferença?

S

fabiocsi:
Sérgio,

No seu exemplo acima, nao estou vendo muita diferença entre seu Repository e um DAO.

Se eu falei besteira, poderia me explicar a diferença?

Repository: Mediador entre a camada de dominio e a camada de mapeamento usando uma interface semelhante à de uma coleção* para acessar objetos do dominio. http://martinfowler.com/eaaCatalog/repository.html

DAO: Data Access Object :

Generalizar a interface de acesso a dados para diferentes mecanismos de acesso a esses dados.

Adapta a API de acesso aos dados persistidos para uma API de acesso a dados (independente de onde estão esses dados) **

http://java.sun.com/blueprints/patterns/DAO.html

  • Uma interface semelhante a uma coleção quer dizer que existem métodos que adicionam o objeto ao repositorio ( add ) , substituem o objecto por uma “copia” mais atualizada (set) , removem o objeto (remove)
    e iteram sobre uma lista de objetos do mesmo tipo (iterate)

** Adaptar interface pode significar adaptar para uma interface do tipo de listagem, mas normalmente não.

A diferença essencial é que um Repository devolve objetos de Dominio e não apenas aglomerados de dados, enquanto um DAO apenas retorna aglomerados dados e não objetos de Dominio.
O fato do repositório usar jdbc diretamente não o torna um DAO. O Objetivo do DAO é isolar/separa a API de persistência de forma que os mesmos dados possam ser persistido de formas diferentes. O objectivo do Repositorio é simular uma coleção de objetos de dominio, se ele o faz usando um Banco de Dados é um detalhe da implementação e não uma definição do que é um Repositorio.

F

Ummm… DAO não retorna objetos de dominio?
Entao isso quer dizer tambem que o método incluir() de um DAO tb nao recebe objetos de dominio?

Teria como vc me mostrar 1 exemplo concreto? queria muito entender isso

S

fabiocsi:
Ummm… DAO não retorna objetos de dominio?
Entao isso quer dizer tambem que o método incluir() de um DAO tb nao recebe objetos de dominio?

Teria como vc me mostrar 1 exemplo concreto? queria muito entender isso

Imagine que tenho um dominio onde existem clientes e pedidos dos clientes. Imagine que quero fazer um report que relacionas os dois. O repositorio permite-me acesso à lista de Cliente e à lista de Pedido. O Pedido tem um cliente associado e um cliente têm vários pedidos associados. Eu navego nesta estrutura diretamente nos objetos de domínio e o Repositório serve apenas para encontrar o objeot “raiz” da minha navegação.

Mas ao criar um report tem um monte de joins que quero fazer e um monte de campos das entidades que não me interessam. Então crio uma frase SQL que me retorne os campos para o report. Como eu não quero usar SQL diretamente e pretendo isolar o acesso a esses dados , uso um DAO. O meu dao , que obtenho de uma fabrica, é configurado externamente para ler/escrever do banco certo no dialecto sql certo. O DAO abstraem para mim os detalhes do acesso as dados, eu apenas passo um critério de procura e ele me retorna os dados. Mas ele retorna os dados em bruto e não disfarçados de objeto de dominio.
Para criar o report eu não quero ter que criar objetos Cliente nem Pedido, apenas quero um conjunto de dados de cada um. Poderia criar um objeto ResultadoDoReportClientePedido com os dados que quero e preencher esse objeto da mesma forma que preencho Cliente ou Pedido, mas ResultadoDoReportClientePedido não seria um objeto de dominio porque não tem nenhum metodo de negocio associado. É apenas um conjunto de dados.
O DAO poderia ser esperto o suficiente para retornar um objeto como este já preenchido, mas um DAO nunca será esperto para distinguir os objetos de dominio.

Entenda que a distinção entre DAO e Repositorio é muito teorica, mas existem diferenças práticas. Estamos falando de padrões e dando exemplos de objetos que implementam um padrão de cada vez. Poderiamos ter um objeto que faz as duas coisas, sendo responsável pela logica de negocio e pelo acesso aos dados. Tudo bem. Não ha problema nisso.
Mas para se possa dizer que esse objeto segue o padrão X ou Y ele tem que ter certas características. Um objecto que contém logica de negocio e acesso a dados junto , nunca será um DAO , porque o objetivo do dao
é poder ter várias implementações que acesso os mesmos dados em locais/estilosl/inguagens diferentes.
Inerante a isto está o uso de um fábrica de DAO e o não uso explicito de nenhuma das implementações possíveis. Logo, por construção e definição se o seu codigo não permite esta estrutura , isso não é DAO.

Mas um objeto que contém logica de negocio e acesso a dados junto pode muito bem ser um Repositorio.

O que precisa ser entendido é que um Repositório pode e deve conter logica de negocio. Um DAO não pode conter lógica de negocio.

Se vc entrar em http://martinfowler.com/eaaCatalog/repository.html verá um uml explicando o Repositorio. Verá que existe uma estratégia de acesso aos dados que o repositorio usa (Strategy Pattern)
Isso singifica que a estratégia pode mudar, mas as ordens e o resultado não podem mudar. Estrategias diferentes correspondem com DAOs diferentes e por isso que Repositorio e DAO não são a mesma coisa.
DAO é um padrão que permite alterar a estratégia de acesso a dados que o Repositorio usa.

Não sei como ser mais claro.

F

Foi bem claro.

Só mais uma pergunta sérgio:

public List<Gerente> lista( StringBuffer sql ) throws Exception {
       
        List listaGerente = new ArrayList();
       
        try {
            
            pstmt = conn.prepareStatement( sql.toString() );
            
            rs = pstmt.executeQuery();
            
            while ( rs.next() ) {
            
                Gerente gerente = new Gerente();
                
                gerente.setId( rs.getLong("ID_GERENTE") );
                gerente.setNome( rs.getString("NM_GERENTE") );
                gerente.setEmail( rs.getString("DS_EMAIL") );
                gerente.setTelefone( rs.getString("NR_TELEFONE") );
                gerente.setRamal( rs.getString("NR_RAMAL") );
                gerente.setCelular( rs.getString("NR_CELULAR") );
                
                listaGerente.add( gerente );
                
            }
            
            return listaGerente;
            
        } catch( Exception e ) {
           
           e.printStackTrace();
           throw new Exception( "Falha ao obter lista Gerentes." );
           
        } finally {

            if ( rs != null ) rs.close();
            if ( pstmt != null ) stmt.close();
            
        }
        
    }

Baseado no que vc me explicou, o código acima nunca poderia ser método de um DAO.

A) Poderia ser método de um Repository?
B) Ou nao pode ser porcaria nenhuma e eu nem deveria fazer isso?

Se a resposta for B, explique com código se possivel como eu poderia fazer certo.

S

Isso não é concerteza um DAO porque não abstrai a interface de acesso aos dados ( ou seja, vc explicitamente usa JDBC). Isso sim pode ser um Repositório. O problema é que :

a)Se vc considera um repositorio, então é um repositório mal implementado porque a sua interface está dependente do método de persistência ( vc passa uma frase SQL)
b)Se vc não considera um repositorio porque está dependente do SQL, então isso não é nem um Repositório nem um DAO.

Você pode :
a) Usar esse objeto sem problema, não pode é dizer que isso é um Design Pattern , nem DAO, nem Repositorio. Mas é válido usar isso. Eu disse válido, não disse bom , nem recomendável.
b) Vc pode retirar StringBuffer e usar uma classe especial de critério. Tipo assim :

public List<Gerente> lista( CriterioDePesquisa criterio ) throws Exception {

Essa classe é um grafo com sub-criterios. Vc pode fazer isso com o padrão Composite. Básicamente vc precisa dos critérios logico e de comparação de valores de campo. Internamente ao método vc traduz isso em num Buffer de sql e continua igual depois disso. (ao fazer isto vc estará usando outro padrão : Intrepreter)
Esta opção é melhor que (a) pois já permite dizer que esse objeto é um Repositorio pois trata os objetos Gerente como se eles estivessem numa coleção.

c) O objeto de critério é muito legal, mas pode haver critérios demasiado complicados para escrever em forma de grafo. Ai vc inclui um método no repositorio que é especifico, ou seja, ele recebe parametros da query e não a query em si. Esta opção é a melhor, vc monta o SQL internamente e só passa os parametros necessários.
Claro que assim vc precisa de um metodo para cada pesquisa. Exemplo:

public List<Gerente> listaTodos() throws Exception 
public List<Gerente> listaGerentes(int  nivel  ) throws Exception 
public List<Gerente> listaGerentesQueAtingiramMeta(Date data, Meta  meta) throws Exception
F

Ummm… ficou mais claro agora…

Porém fazendo isso:

public List<Gerente> listaTodos() throws Exception   
public List<Gerente> listaGerentes(int  nivel  ) throws Exception   
public List<Gerente> listaGerentesQueAtingiramMeta(Date data, Meta  meta) throws Exception

em todos eles eu vou ter isso:

while ( rs.next() ) {   
             
              Gerente gerente = new Gerente();   
                 
              gerente.setId( rs.getLong("ID_GERENTE") );   
              gerente.setNome( rs.getString("NM_GERENTE") );   
              gerente.setEmail( rs.getString("DS_EMAIL") );   
              gerente.setTelefone( rs.getString("NR_TELEFONE") );   
              gerente.setRamal( rs.getString("NR_RAMAL") );   
              gerente.setCelular( rs.getString("NR_CELULAR") );   
                 
              listaGerente.add( gerente );   
                 
       }

fica feio nao? :?:

S

fabiocsi:

em todos eles eu vou ter isso:

while ( rs.next() ) {   
             
              Gerente gerente = new Gerente();   
                 
              gerente.setId( rs.getLong("ID_GERENTE") );   
              gerente.setNome( rs.getString("NM_GERENTE") );   
              gerente.setEmail( rs.getString("DS_EMAIL") );   
              gerente.setTelefone( rs.getString("NR_TELEFONE") );   
              gerente.setRamal( rs.getString("NR_RAMAL") );   
              gerente.setCelular( rs.getString("NR_CELULAR") );   
                 
              listaGerente.add( gerente );   
                 
       }

fica feio nao? :?:

:shock: ora … que tal um método assim :

// algum metodo faz
      while ( rs.next() ) {   
              listaGerente.add( parse(rs));    
       }
//---

protected Gerente parse(ResultSet rs ){
             Gerente gerente = new Gerente();   
                 
              gerente.setId( rs.getLong("ID_GERENTE") );   
              gerente.setNome( rs.getString("NM_GERENTE") );   
              gerente.setEmail( rs.getString("DS_EMAIL") );   
              gerente.setTelefone( rs.getString("NR_TELEFONE") );   
              gerente.setRamal( rs.getString("NR_RAMAL") );   
              gerente.setCelular( rs.getString("NR_CELULAR") );   
                 
             return gerente;
}

Este método vc pode até substituir por algo usando Reflection e Annotations
Vai, não é assim tão feio … :smiley:

F

:smiley: rox

Ultima pergunta:

Digamos que eu queira usar isso com Reflection, mas sem Annotations… pode me da uma luz?

S

fabiocsi:
:smiley: rox

Ultima pergunta:

Digamos que eu queira usar isso com Reflection, mas sem Annotations… pode me da uma luz?

Com ou sem anotações o objetivo daquele pedaço de codigo é o quê ? É mapear os campos da tabela para os campos no objeto de dominio.
Na maioria dos casos esse mapeamento é 1 para 1 , mas não será sempre. A resposta é portanto : crie um mappeador. Este objeto tem a responsabilidade de saber como e fazer o mapeamento entre os dados no ResultSet e os campos do objeto e vice-Versa. NO caso inverso seria um mapeamento para um Statement ou um PreparedStatement.

interface DataMapper 

<T> T read(Class<T> , ResultSet rs );

Agora é s´po implementar.

Sem anotações vc precisará de um lugar onde guardar o mapeamento. A escolha obvia é XML (!) a solução mais simples
sem usar XML é covencionar que os nomes dos campos nos objectos serão os nomes nas tabelas. Isso funciona em muitos casos
mas o mapeamento XML é para casos complexos em que os campos não podem ter os mesmos nomes (por alguma razão, não interessa qual)

Então, usando reflection vc pega os métodos chamados getXXX retira o XXX , pega o XML ou outro formato qq, traduz esse XXX para YYY e usa o YYY para criar o SQL que passa para o banco. Seja usando o nome do get, o xml ou anotações o processo é basciamente o mesmo, é uma tradução de nomes. Claro, isto é o básico, tem mais coisas , como traduzir os nomes das tabelas elas mesmas , ai o nome da classe tem que ser traduzido para o nome da tabela.

Acho que não precisa ir muito longe, basta usar as anotações do JPA e implementar um mapeamento para elas. É mais ou menos linear para os casos simples. E os casos complexos vc pode sempre implementar uma função especial dentro do Repositorio.

Y

fabiocsi:
Sérgio,

No seu exemplo acima, nao estou vendo muita diferença entre seu Repository e um DAO.

Se eu falei besteira, poderia me explicar a diferença?

Corrijam-me se eu estiver errado, mas a diferenca de um DAO e um Repository eh a finalidade de cada um. Eles podem ser implementados exatamente da mesma maneira, mas o intuito eh diferente.

O q eu faco eh usar os dois. Um repository q conhece uma interface DAO. Amanha se eu quiser mudar a forma de implementar a persistencia por tras dos repositories (deixar de usar o pattern DAO e usar qqr outro q um dia possa aparecer) meu objetos de dominio nao sofrerao em nada.

S

YvGa:
fabiocsi:
Sérgio,

No seu exemplo acima, nao estou vendo muita diferença entre seu Repository e um DAO.

Se eu falei besteira, poderia me explicar a diferença?

Corrijam-me se eu estiver errado, mas a diferenca de um DAO e um Repository eh a finalidade de cada um. Eles podem ser implementados exatamente da mesma maneira, mas o intuito eh diferente.

Não. O intuito é diferente e a implementação é diferente. Não faz sentido criar uma factory de repositorios pelo simples motivo que a lógica contida neles é a logica do negocio. Se as lógicas mudam, os repositórios mudam, não se criam outros.
Enquanto nos DAO a logica é sempre a mesma, é invariante, a implementação é diferente conforme a API real
Ou seja um XMLDAO e um JDBCDAO têm a mesma interface e procuram os mesmos dados , apenas os procuram em lugares distintos e com API distintas. É neste sentido que digo que a logica é invariante.
A logica dos Repositorios pode mudar conforme a lógica do negocio, ou porque contém logica que tem que se modificada ou porque as entidades com que eles trabalham foram modificadas.

DAO é uma coisa plugável. Atrás de um DAOFactory todos os DAO são iguais.
Repositorio não é plugável, ele faz parte do coração da aplicação.

Reaproveitar logica de negocios não é simples, e normalmente não se faz. Logo cada sistema terá seus repositorios proprios.

J

Voltando ao assunto:
Chegamos a 3 modelagens eu acho, sendo assim em uma tentativa modesta vou tentar apontar os pros e contras de cada um … alias quando eu tiver um tempo vou abrir um tópico somente para discussão do assunto: ?Cliente Pessoa uma modelagem mal vista? e lá sim vou colocar os pros e contras.
Aqui vou somente perguntar.

Tb concordo q com certeza não existe interface comum entre pessoa e empresa, seria muito forçado abstrair esses dois. Logo PessoaFisica e PessoaJurica são conceitos fiscais(inclusive de cada pais). Por chamo as classes de empresa e pessoa.
Desculpe estar sendo redundante ou especificando detalhes que parecem ridículo, mas como eu já disse estou apavorado com o que aparece de modelagens sem nexo na internet. Acho q por isso POO tem um efeito contrario quando usado por determinadas empresas.
Bom vamos lá.

Na imagem A temos a modelagem que eu escolhi. Fora as questões que o Sergio já resolveu, inclusive com muita propriedade, fico em duvida: Optando pela modelagem A posso criar um método na minha interface Cliente chamado Tp_P_E : Integer;
Assim quando implementado por ClienteEmpresa ou ClientePessoa ele retorna ?0? quando o Cliente for herdado de Empresa e ?1? quando herdado de Pessoa. Logo quando eu estiver manipulando a interface Cliente saberei por exemplo chamar a pela de cadastro de ClientePessoa ou ClienteEmpresa. Aqui temos o inverso do que estávamos tratando antes. A questão é, esse método Tp_P_E é uma injambração ou é valido. A esse método seria publico.
A quanto ao mapeamento, no BD, usando a Opção A, Tenho uma Tabela Pessoa e outra ClientePessoa. ClientePessoa tem o mesmo ID que Pessoa assim estabeleço a relação. O Sergio comentou em fazer uma única Tabela Cliente, porem se Pessoa é uma Classe que pode persistir não ficaria mais fácil pensando no bd ter uma tabela Pessoa e ClientePessoa. Ou tb poderia ter tabela Pessoa somente para a classe pessoa, e Cliente para as classes ClientePessoa e CLienteEmpresa e para interface Cliente tb.

A quanto a modelagem C q seria algo parecido com a que o YvGa porem fiquei em duvida como implementar um interface para o tipo (Empresa ou Pessoa) vai usar. Se alguém já usou e tiver algum comentário por favor fique a vontade.


S

Joaquimnabuco:
Voltando ao assunto:
Chegamos a 3 modelagens eu acho, sendo assim em uma tentativa modesta vou tentar apontar os pros e contras de cada um … alias quando eu tiver um tempo vou abrir um tópico somente para discussão do assunto: ?Cliente Pessoa uma modelagem mal vista? e lá sim vou colocar os pros e contras.
Aqui vou somente perguntar.

O dificil de responder é que não existe ‘A’ solução , sempre existe "UMA’ solução. Qual é a solução para o modelo de “pessoa” para um ERP é diferente da de “pessoa” para uma agenda.

Em traços gerais clientes são capacidades das pessoas A pessoa é cliente ou não é independnete de ser cliente.
Aliás o conceito de prospect é o de uma pessoa que ainda não é cliente mas é interessante à empresa. Concerteza não foi ter um cadastro complexo de prospect, o mais será o nome e contacto como numa agenda. Mas após a primeira venda essa mesma pessoa é agora um cliente. É como se “cliente” fosse um estado de "pessoa"
Pessoas podem ser prospects , clientes , ex-clientes. Podem, simultameamente ser fornecedores , vendedores, empregados, etc… Seja qual for o(s) papel(eis) que a pessoa representa para o sistema, os dados cadastrais da pessoas são os mesmos.

Tem que saber , para começar , o objetivo do modelo, pois não ha um modelo bom para todas as situações.
Quer dizer, eu acho que ha, só que não será simples. A complexidade de tal modelo pode ser importante num ERP por exemplo que tem que lidar com vários aspectos da empresa, mas numa agenda é overkill.

Y

sergiotaborda:
YvGa:
fabiocsi:
Sérgio,

No seu exemplo acima, nao estou vendo muita diferença entre seu Repository e um DAO.

Se eu falei besteira, poderia me explicar a diferença?

Corrijam-me se eu estiver errado, mas a diferenca de um DAO e um Repository eh a finalidade de cada um. Eles podem ser implementados exatamente da mesma maneira, mas o intuito eh diferente.

Não. O intuito é diferente e a implementação é diferente. Não faz sentido criar uma factory de repositorios pelo simples motivo que a lógica contida neles é a logica do negocio. Se as lógicas mudam, os repositórios mudam, não se criam outros.
Enquanto nos DAO a logica é sempre a mesma, é invariante, a implementação é diferente conforme a API real
Ou seja um XMLDAO e um JDBCDAO têm a mesma interface e procuram os mesmos dados , apenas os procuram em lugares distintos e com API distintas. É neste sentido que digo que a logica é invariante.
A logica dos Repositorios pode mudar conforme a lógica do negocio, ou porque contém logica que tem que se modificada ou porque as entidades com que eles trabalham foram modificadas.

DAO é uma coisa plugável. Atrás de um DAOFactory todos os DAO são iguais.
Repositorio não é plugável, ele faz parte do coração da aplicação.

Reaproveitar logica de negocios não é simples, e normalmente não se faz. Logo cada sistema terá seus repositorios proprios.

Acho q exagerei ao dizer “exatamente da mesma maneira”, mas ficou uma duvida especificamente qto a forma q estou utilizando.

Realmente nao os implemento da mesma maneira, meus Repositorios nao tem factory, nem sao abstratos, e como vc disse, mudam se algo mudar nas regras de negocio.

O q eu faco eh fazer com q os repositorios instanciem e executem metodos nos DAOs, portanto eles tem metodos com mesmo nome e assinatura q eu tenho nos DAOs, por exemplo, o repositorio.salvarCliente(cliente), em sua implementacao salva o cliente atraves de um metodo salvarCliente no DAO. Ha algo nisso tudo q eu estou perdendo?

S

YvGa:

O q eu faco eh fazer com q os repositorios instanciem e executem metodos nos DAOs, portanto eles tem metodos com mesmo nome e assinatura q eu tenho nos DAOs, por exemplo, o repositorio.salvarCliente(cliente), em sua implementacao salva o cliente atraves de um metodo salvarCliente no DAO. Ha algo nisso tudo q eu estou perdendo?

No método salvar do repositorio ( que deveria ter um nome menos ligado a persistência e mais ligado a operações de conjuntos) realmente não ha muito a fazer do que delegar para o DAO. A menos, é claro, que exista um trigger qualquer no momento da inserção ou modificação da entidade no repositorio. Nos casos simples a delegação resolve.
Contudo, a necessidade de existir um Repositorio vem do uso de aggregados, ou seja, grafos de vários objetos cujos estado é interligado. Ao persistir a raiz do grafo têm que ser tb persistidas as folhas. Ou não. A logica vai depender do que é o grafo e como o aggregado é montado. É esta tradução complexa que exige o uso de Repositorio.

Sem essa complexidade, não ha necessidade de um repositório que apenas delegue para o DAO. Bom, isso é que dizem. Eu acho que mesmo assim vale a pena ter um repositório genérico que faça essas operações básicas de delegação. Isto porque um dia mais tarde elas podem deixar de ser simples e a delegação insuficiente.

Vc tem então duas opções. 1) só usar repositorio quando existe uma complexidade inerente às entidades e suas classes relacionadas. 2) não usar repositorio quando tudo o que ele faz é delegar para o DAO.

Mas , entenda-se que na opção 2 estaremos pulando uma camada e isso poderá ser prejudicial. Fora que, ao usar um misto, poderá complicar a manutenção/evolução do sistema.

F

Sérgio:

Então se eu tenho um objeto Pessoa, e esse objeto possui um List de objetos Endereco…
Eu teria um PessoaDAO, um EnderecoDAO, e o meu PessoaRepository teria por exemplo um
método getById que chamaria os DAOS e me devolveria o objeto Pessoa com seus enderecos?

Y

fabiocsi:
Sérgio:

Então se eu tenho um objeto Pessoa, e esse objeto possui um List de objetos Endereco…
Eu teria um PessoaDAO, um EnderecoDAO, e o meu PessoaRepository teria por exemplo um
método getById que chamaria os DAOS e me devolveria o objeto Pessoa com seus enderecos?

Pelo q eu entendo eh isso mesmo.

A unica coisa eh q eu nao uso um EnderecoDAO, por endereco ser Value Object (pelo menos no meu dominio) entao, o PessoaDAO eh quem vai ser responsavel por persistir e recuperar a lista de enderecos.

S

"fabiocsi ":
Então se eu tenho um objeto Pessoa, e esse objeto possui um List de objetos Endereco…
Eu teria um PessoaDAO, um EnderecoDAO, e o meu PessoaRepository teria por exemplo um
método getById que chamaria os DAOS e me devolveria o objeto Pessoa com seus enderecos?

Acho que o YvGa repondeu a essa questão. Se , se , Endereço é em objeto de valor (Value Object) que pertence ao grafo ( Aggregation) de Pessoa, então é o repositório de Pessoa que deve procurar os endereços da pessoas e adicioná-los ao objeto pessoa.
Se , se, Endereço é tb uma entidade ele precisa do seu repositorio.

Então PessoaDAO é na realidade o seu PessoaRepository. Que sistema de persistencia vc usa no PessoaDAO ?

F

Tb concordo com o Sérgio.

Seu PessoaDAO ta fazendo a funcao do PessoaRepository.

Bom, pelo que tenho aprendido nesse topico, nem PessoaDAO nem EnderecoDAO devem envolver objetos de domínio.

Y

RepositorioPessoa delega para a interface PessoaDAO q persiste o objeto Pessoa e seus List. Eu uso na implementacao de PessoaDAO hibernate.

F

Entao me diga o que seus Repositorios fazem, ALÉM de delegar.

Y

fabiocsi:
Tb concordo com o Sérgio.

Seu PessoaDAO ta fazendo a funcao do PessoaRepository.

Na verdade nao. Apenas tenho os dois. O Repositorio delega a acao ao DAO.


Bom, pelo que tenho aprendido nesse topico, nem PessoaDAO nem EnderecoDAO devem envolver objetos de domínio.

Talvez eu esteja errado, mas nao vejo problema nisso. Acho q o problema esta qdo os objetos de dominio conhecem os DAOs.

S

Se eu implementasse a sua interface DAO para PessoaDAO usando JDBC puro, seu sistema funcionaria ?
Como a minha implementação saberia que é responsável por recuperar/guardar tb os endereços ?
A resposta é: se souber existe uma violação de camadas, porque está sendo deixada logica referente ao dominio (no caso a dependencia que pessoa tem de endereço) dentro do DAO, que não pertence ao dominio.

Ora, se eu não posso substituir o DAO por causa desse problema, então não ha porquê ter um DAO. Faça essa logica diretamente no repositorio. não ?

Y

Vou citar o q o Sergio disse aqui sobre isso.

Y

Nao tinha visto essa diferenca sutil. Como eu uso hibernate, um session.save no DAO resolve o problema e eu nao preciso me preocupar com a forma q os enderecos serao gravados.

Se nao fosse o hibernate realmente concordo com vc q nao funcionariae o codigo sim deveria ser implementado no respositorio. A pergunta eh: eu sempre tenho q tratar disso no repositorio, mesmo q (como no caso do hibernate) eu nao precise, apenas pela possibilidade de um dia precisar? E se eu quisesse implantar com jdbc estaria errado em tbm delegar essa tarefa ao DAO? Mas se nao, como eu implementaria de diversas formas, diversas tecnologias? Nao seria util esconder essa implementacao ate do repositorio?

Agora ficou a duvida.

S

O hibernate é um hibrido. Ele não é apenas um DAO, ele é tb um manager. No caso do Hibernate não faz sentido usar DAO, apenas Repository que delega para o Hibernate diretamente.
Mas isso é ruim se eu quiser mudar meu mecanismo de persistência ! - dirá vc.

Pois é. O problema é que se vc usa hibernate vc ja está amarrado a um mecanismo.
Foi o exemplo que dei, se mudar a implementação da interface para JDBC puro não vai funcionar porque o JBDC puro não tem metadados de relacionamento, não controla se o objeto está “dirty” etc… , ou seja, não é um manager. Na prática ao usar hibernate o sistema está livre para usar qq banco, mas não para usar qq sistema de persistência. Se o objetivo é sempre usar Banco de Dados, usar o hibernate não é um problema. Só é problema no caso generico de poder mudar o DAO para qualquer sistema de persistência.

Se o meu sistema tem que funcionar em qualquer mecanismo de persistência, então o repositório não pode assumir que o DAO fará o trabalho X ou Y e tem que tratar o DAO como um burro de carga entre a persistencia e a memória.
Se o seu sistema funciona apenas com Banco de Dados Relacionais suportados pelo Hibernate , use o hibernate dentro do repositorio directamente. Isso implica que o repositorio pode tirar partido das capacidades especiais do hibernate, e neste caso não faz sentido usar o padrão DAO.

Y

sergiotaborda:

Se o meu sistema tem que funcionar em qualquer mecanismo de persistência, então o repositório não pode assumir que o DAO fará o trabalho X ou Y e tem que tratar o DAO como um burro de carga entre a persistencia e a memória.
Se o seu sistema funciona apenas com Banco de Dados Relacionais suportados pelo Hibernate , use o hibernate dentro do repositorio directamente. Isso implica que o repositorio pode tirar partido das capacidades especiais do hibernate, e neste caso não faz sentido usar o padrão DAO.

Eu ou dar mais uma olhada na definicao de repositorio e depois tornamos a discussao. Mas a principio eu discordo pois acho q os detalhes de como os objetos sao persistidos e carregados podem ser responsabilidade dos DAOs.

E se eu quiser usar outro db (db4o por exemplo, q ando testando) eu tenho q reescreer todos os meus repositorios? Ou pior, usar meus repositorios com factories e interfaces, transformando-os erdadeiramente em DAOs.

S

YvGa:
sergiotaborda:

Se o meu sistema tem que funcionar em qualquer mecanismo de persistência, então o repositório não pode assumir que o DAO fará o trabalho X ou Y e tem que tratar o DAO como um burro de carga entre a persistencia e a memória.
Se o seu sistema funciona apenas com Banco de Dados Relacionais suportados pelo Hibernate , use o hibernate dentro do repositorio directamente. Isso implica que o repositorio pode tirar partido das capacidades especiais do hibernate, e neste caso não faz sentido usar o padrão DAO.

Eu ou dar mais uma olhada na definicao de repositorio e depois tornamos a discussao. Mas a principio eu discordo pois acho q os detalhes de como os objetos sao persistidos e carregados podem ser responsabilidade dos DAOs.

E se eu quiser usar outro db (db4o por exemplo, q ando testando) eu tenho q reescreer todos os meus repositorios? Ou pior, usar meus repositorios com factories e interfaces, transformando-os erdadeiramente em DAOs.


Não. Esse é exatamente o ponto. VC vai escrever apenas outro DAO, o repositório é mesmo sempre. Por isso que o DAO não pode conter logica de dominio , de forma que possa ser substituido. Mas o repositorio pode e deve ter essa logica.

A dependencia que existe entre uma classe e outra é um conhecimento de dominio e não de persistencia. O nome da tabela, arquivo , url , sei lá … onde os objetos de dominio sem guardados é conhecimento do mecanismo de persistencia e não do dominio.
Os detalhes da carga são da responsabilidade do DAO, mas o relacionamento entre as classes/entidade não é um detalhe da carga.

Y

fabiocsi:
Sérgio:

Então se eu tenho um objeto Pessoa, e esse objeto possui um List de objetos Endereco…
Eu teria um PessoaDAO, um EnderecoDAO, e o meu PessoaRepository teria por exemplo um
método getById que chamaria os DAOS e me devolveria o objeto Pessoa com seus enderecos?

Certo, Sergio. Mas esse eh o ponto onde comeca a discussao. Nesse caso, o PessoaDAO nao pode ser o responsavel pela reconstrucao do objeto Pessoa? Nada de errado em o Repositorio ter essa funcao, mas nao ficaria melhor no DAO (a nao ser, obviamente, q eu nao queira recuperar a lista de endereco na hora da construcao do objeto Pessoa).

Eu vejo o padrao DAO como a ponte entre um objeto concreto e sua persistencia, e nesse caminho q ele se “desintegra” e vai para o mundo relacional. E na volta se reintegra para o mundo dos objetos. O responsavel por isso seria o DAO e nao repositorio, certo?

Tudo muda qdo eu nao quero recuperar a lista de enderecos no instante da criacao do objeto, nesse caso, sim o repositorio eh quem tem a responsabilidade de recompor, aos poucos, o objeto.

S

Não. Se o DAO tem como missão traduzir os seus objetos de negocio para a persistencia e vice-versa ele seria um tradutor. Todo o tradutor precisa conhecer as duas linguas envolvidas. Significa portanto que o DAO conheceria sua linguagem de negocio ( o seu dominio). Ora, vc não deve querer isso.
Se o DAO conhece o dominio, vc não pode plugar outro DAO.

Imagine assim:
Vc tem a aplicação A e a B. A é uma aplicação financeira e seu dominio são contas , transções, saldos etc. B é uma aplicação de vendas e seu dominio são produtos, itens, pedidos, orçamentos, etc…

Obviamente as lógicas de dominio são diferentes. Elas são encapsuladas em repositorios além de nas respectivas entidades e demais objetos do dominio.

Agora imagine que vc tem um SQLServer e vc cria um DAO para ele. Ambos, A e B serão persistidos no SQLServer (em bancos diferentes, claro). O DAO é o mesmo. Tanto para A como para B.
Este é o objetivo do DAO, ser substituivel e reutilizável.

Agora pense que alguem resolve matar o SQLServer e subtituir por PostgreSQL. Vc não muda a sua aplicação, vc apenas muda o DAO. Que continua sendo o mesmo para A e B.
Repare que o codigo dos repositórios não mudou , nem o das entidades.

Agora pense que alguem quer fazer um demo da aplicação A. O demo deverá correr sobre HSQL embarcado.
Vc cria um terceiro DAO para HSQL. Não ha mudança nenhum das regras de negocio. Apenas das interfaces de persistência.

Para que este troca-troca dos DAO seja possível, eles devem ignorar ao máximo os objetos de dominio.
Assim os DAO são apenas burros de carga. Se eles são burros , onde fica a inteligência do dominio ? Nos repositorios. Essa inteligencia, essa logica, é igual , quer a pesistencia seja SQLServer,HSQL, Potgress ou até memso XML , Prevayler, DB4O , etc … É até a mesma se o DAO for volátil ( ou seja, que não persiste verdadeiramente)

F

Sérgio:

um metodo lista() de um DAO retorna o q? Object[]?

S

fabiocsi:
Sérgio:

um metodo lista() de um DAO retorna o q? Object[]?

Vc que sabe, vc que define a API do DAO. Object será meio chato.
A API do DAO tem que ser tal que sua interface possa ser implementada com várias API sujacentes, já que o DAO,
na realidade é um Adapter ( ele transforma a interface uma API na interface de outrao API) só que ele mesmo define a API final.

A minha sugestão seria devolver algo como RecordSet ou RecordList , sendo estes coleções de Record que contém um conjunto de campos e valores.

Y

Entao eu realmente estou implementando o DAO de forma errada, pq ele conhece os objetos de dominio e eu nao conseguiria usar os mesmos DAOs em aplicacoes tao diferentes qto as q vc citou.

Eu tenho uma interfaceDAO q retorna, por exemplo, um objeto Venda. Eu posso mudar de banco de dados qtas vezes eu quiser apenas acrescentandos novas implementacoes a essa interface. Mas eu nao consigo reutiliza-las com outras regras de negocio, num outro dominio, vou precisar de outros DAOs.

Se vc puder me dar algum exemplo do q vc diz pq eu nao consegui ainda visualisar bem (a nao ser fazendo typecasts pra todo lado, mas ai vale a pena?).

Na pagina da Sun em q descreve o Pattern DAO eu encontrei la pelas tantas:

public interface CustomerDAO{

public int insertCustomer(…);

public boolean deleteCustomer(…);

public Customer findCustomer(…);

}

Repare como na propria documentacao do Pattern a interface retorna um objeto do dominio.

A

Velhas discussões em novos tópicos.

Que tal estudar os exemplos do Hibernate/JBoss (se é que estão usando uma boa ferramenta ORM)!? :roll:

S

Veja, não ha nada de errado com os objetos que vc usa. Apenas eles não são implementações do padrão DAO e sim do Repository.
Não significa que vc tem que mudar seu sistema. Apenas os nomes das classes :lol:

Pois, so que esse objecto é na realidade um DTO. Ou , no minimo, não segue DDD E ai que a coisa se complica.
Objetos de domino todos são. Afinas suas regras, interfaces e estados dependem do dominio. Ninguem saria Pedido e ItemPedido
num sistema de banco. Sejam eles DTO, Entity , Aggregation ou qq outro padrão. No fim, não importa.

O dilema aprece se queremos misturar os conceitod de DDD com as práticas e tecnologias actuais da Sun.
Não ha no mercado - até onde sei - uma implementação genecia do DAO. Aliás se houvesse isso
seria quase um contrasenso.
O que temos sõa frameworks que usar objetos dos dominio como se fossem DTO e vendem a ideia de
que porque esses objetos têm metodos que atuam sobre o estado do objeto, então esses objetos são objetos de dominio completos e sem
nenhum problema. Ora isso não é verdade. Muito já se falou do problema do JPA/Hibernate não funcionar “out-of-the-box” em ambiente distribuido
Os famosos detached (ou melhor un-managed) são objetos verdadeiramente de dominio , independentes da persistencia, que esses framewrks tem problemas em tratar.

Não ha problema em usar essas tecnologias se é isso que realmente se precisa e quer, mas é importante saber o que elas são e que trade-offs fazem.
Acho que ninguem espera um JPA para XML … em tese poderia existir, mas não é isso que está na cabeça da pessoa ao pensar em JPA. Pensa-se me SQL e banco de dados. A prova é que vc define queries em quase-SQL , não em quase-XPath

Também é verdade que sistemas persistidos em outra coisa que não em banco não são muito levados a serio e portanto o esforço de criar uma api hipergenerica de DAO não é compensador. Mas falávamos de padrões e nao de tecnologias.
Agora qe vc entendeu o padrão e as diferenças subtis entre eles ao menos não vai achar que DAO genérico é algo que usa hibernate.

Ainda sobe outro ponto de vista tem o problema do DDD. NA superficie DDD é muito legal. Mas DDD acaba por ser uma filosofia , uma forma de vc encarar seus objetos e suas responsabilidades, mas não é um padrão tecnologico.
Ele não ele diz como implementar os objetos. Mas ele diz como os construir.

É aqui que mora o problema. Se a construção depende das relações de agregação, composição e associação entre as entidades e logo, entre as classes, e isso é modelado de alguma forma no dominio. Como?
Quando DDD nasceu annotations ainda não eram comuns. Portanto a responsabilidade de donhecer os metadaos tinha que estar em algum objeto : o repositorio.

Essa informação é o que o sistema de persistencia precisa para o seu trabalho. E na forma de metadados embutidos , que são as anotações eles funcionam. Mesmo com os metados em xml ou noutro formato. O ponto é que o ORM precisa de metadados, o Repositorio não.

O repositorio com várias estratégias como no modelo do Fowler pode ser uma utopia em sistemas do dia-a-dia, e o repositorio eu-faço-tudo poderá ser um opção mais válida. Sobretudo usando hibernate. O ponto, que espero que seja claro é que não dá para ter as duas coisas, e é necessário fazer escolhas.

YvGa se o seu sistema está rodando com DAO dessa forma otimo. Apenas declare que está seguindo o padrão da Sun , que parece usar objetos de dominio e que não está usando DDD. É só uma questão de por as coisas em contexto. Algum vai dizer que DAO é do passado ,mas vc saberá no fundo que aquilo é um repostório que usa o nome trocado… e que nunca funcionará para outra persistencia que não Banco de Dados. Precisaria ? :wink:

A

Em qual referência “DDD” esta descrito que DAO não pode retornar objetos de negócio Sergio?
DAO na maioria de vezes é um gerenciador de entidades persistentes que utiliza DataMapper, portanto pode sim retornar objetos de negócio (eu disse na maioria das vezes).

O Repository é uma abstração disso, podendo utilizar DAOs para atender uma determinada ação de negócio.

A

Um Client de um DAO deve ser um Repository …

S

Lezinho:
Em qual referência “DDD” esta descrito que DAO não pode retornar objetos de negócio Sergio?

Em qual referencia de DDD está escrito que se deve usar DAO ? Onde o padrão DAO é mensionado ?

E se o DAO pode retornar objetos do dominio porque ele não é um objeto do dominio? E se ele é um objeto de dominio que retorna objetos de dominio qual o papel do Repository (que é mencionado explicitamente várias vezes) ?

A

Pra mim, o padrão é implícito toda vez que a junção dos padrões Query Object e DataMapper é utilizado, mas se não quiser cunhar como DAO pode chamar de DataMapper + QueryObject. Contudo, a essência destes padrões é encapsular o acesso ao dados (por isso cunho como DAO), e não realizar lógica de negócio.

Ele não é um objeto de domínio pq é apenas um Helper de acesso a dados, não executa lógica alguma e não tem valor ao modelo. Portanto ele não é um objeto de domínio, e por isso existe o Repository.

Repository pode executar instruções relacionadas ao negócio, pq faz parte dele. Provavelmente, para que esta consulta ocorra, ele deve solicitar a um ou vários DAOs (DataMappers + Query Objects) a tradução do serviço.

Mas eu entendo o que você vem dizendo Sergio de uma implementação onde a interface de um repository seja implementado diretamente por um JPA ou Hibernate da vida, entendo porém não apoio. Acredito ser mais interessante a implementação de um repository ter por composição um ou mais DAOs injetados e o Repository fazer uso deles, sem saber se esta trabalhando com EntityManager, Session ou qualquer coisa do gênero.

… IMHO.

S

Lezinho:
Pra mim, o padrão é implícito toda vez que a junção dos padrões Query Object e DataMapper é utilizado, mas se não quiser cunhar como DAO pode chamar de DataMapper + QueryObject. Contudo, a essência destes padrões é emcapsular o acesso ao dados (por isso cunho como DAO), e não realizar lógica de négócio.

Humm… Repository tb é um DataMapper (Afinal criar um aggregado é um mapeamento) e tb pode usar QueryObject , contudo não é um DAO.

Eu não estou defendendo que se use o Repository como uma casca para o JPA/Hibernate.
O que eu estou dizendo é que em vez de usar um DAO (que encapsula o acesso a dados) vc pode não encapsular e acessar a API de dados diretamente no Repository. Ou seja, se fosse o JDBC vc pode escolher usar
Repository -> JDBC ou Repository -> DAO -> JDBC.

O Eric o Fowler usam o termo (Repository)Strategy para se referirem às várias formas de acessar os dados

Mas isso é apenas um adjetivo para DAO. Como falei. O termo "DAO" nunca é usado no texto.
Ele diz ainda o seguinte:

O que significa que o Repository é além de objeto de dominio, objeto de estrutura.
É o que acontece se usar JPA ou Hibernate directamente nele. Mas usando o DAO/Strategy isola a dependência da estrutura, o que eu acho mais limpo.

[size=9]Corrigidos os creditos da citações[/size]

A

É exatamente o que faço quando opto por utilizar composição na implementação de meu Repository (injetando o DAO como atributo). A Strategy é a interface DAO injetada como atributo do meu repository, o encapsulamento de acesso fica neste DAO, assim como vc lembrou na citação.

sergiotaborda:

O que significa que o Repository é além de objeto de dominio, objeto de estrutura.

Não, o que a citação diz é que o Repository [B]PODE[/B] conter os detalhes da infraestrutura, não que DEVE conter. Como eu disse anteriormente, eu acho que a primeira citação mencionada, usando uma Strategy, mais adequada.

Se eu tenho como Strategy uma interface de DAO, logo ele pode sim retornar uma entidade que representa um objeto de domínio para meu repository, como disse em meu primeiro posto neste tópico.

PS: Quem escreveu Domain-Driven Design Quickly não foi Eric Vans, mas sim Avram e Marinescu.

A

Só é um DataMapper se ele possuir os códigos de infraestrutura da ponte Objeto / Relacional, se não ele apenas faz uso de um DataMapper. Ele não realiza mapeamento de camadas, alguém realiza pra ele.

S

É exatamente o que faço quando opto por utilizar composição na implementação de meu Repository (injetando o DAO como atributo). A Strategy é a interface DAO injetada como atributo do meu repository, o encapsulamento de acesso fica neste DAO, assim como vc lembrou na citação.

Estamos todos de acordo então.

Estamos de acordo tb. Me expressei mal. Eu queria dizer : “O que significa que o Repositorio que conem informações de de infraestrututra é alem de objeto de dominio, objeto de estrutura”

Bom, é ai que discordamos. Se olhar bem a definição da responsabilidade do repositorio é encontrar e criar os agregados. Ele é como um cache que cria o objeto apartir da persistencia quando não o acha “em memoria”.
Essa criação é ajudada por Factories. Mas a logica é contida no Repositorio. Ou seja, o Repositorio é o cara que sabe montas os legos que são os objetos das entidades e seus respectivos value-objects.
Um exemplo simples: A conta tem um saldo. Esse saldo é calculado fazendo um sum() dos movimentos. No banco isso é a soma de uma coluna numerica. O repositorio traz isso facilmente do banco usando um select sum() etc… Mas o meu modelo não usa numeros sem unidades e todas as quantidades monetárias são inseridas num value object Money. O repositorio é o cara que sabe esta regra e sabe como contruir esse objeto. Money não é persistente e é um Value Object do dominio

// no repositorio de conta

public Conta find(AccountNumber number){

     BigDecimal saldo = // operação com dao que retorna um numero

     Conta conta = ContaFactory.createNew();

     conta.saldo = new Money ( saldo , "BRL");

}

Isto é um exemplo tosco para é só para mostrar que o DAO não tem como saber a moeda em que o saldo é ditado, nem que o saldo é na realidade um Money. Outros Value Objects mais complexos encontrados dos livro deixam mais clara o papel do repositorio. O Strategy/DAO só tras dados brutos, é o Repositorio que os intrepreta.
(a moeda poderia ser ainda lida de uma tabela setup ou de uma outra entidade)

Se o DAO souber tudo isto, ele contém logica de dominio. E isso, acho que concordamos, é uma violação do padrão DAO.

Y

O q particularmente eu acho muito mais intuitivo, tirando do repositorio a responsabilidade de fazer o mapeamento. Dessa maneira ele soh delega e continuara delegando seja qual for a infraestrutura q eu venha a adotar.

Em um dos posts o Sergio disse q eu deveria usar diretamente o Hibernate com o repositorios “pulando” o DAO. Eu discordo, ou prefiro nao fazer assim, pq um dia eu posso mudar minha ferramenta de persistencia e entao terei q mudar toda a implementacao dos repositorios (fechado para modificacoes, aberto para extensoes).

Vc vai dizer tbm q nao se deve lidar hj com o q pode vir um dia. Mas nao ha nada mais propenso a mudancas do q uma ferramenta de terceiros, q pode ser superada por outra (em custo, implementacao, qualidade etc…) a qualquer momento.

Y

sergiotaborda:

Se o DAO souber tudo isto, ele contém logica de dominio. E isso, acho que concordamos, é uma violação do padrão DAO.

Qual a necessidade de ter DAO entao? No meu modo de ver sao eles podem ser os responsaveis pela construcao dos objetos e entrega-los aos repositorios ja em estado valido. (Nao q os repositorios nao possam eles mesmos fazer isso), mas como ja disse antes - particularmente prefiro q os DAOs facam.

Tbm discordo Sergio qdo vc diz q os DAOs nao devem conhecer os objetos de dominio, eles fazem parte da camada de persistencia q pode perfeitamente conhecer a camada de dominio - o contrario eh q nao eh aconselhavel. O q nao pode realmente e a persistencia conter logica de dominio, mas transformar algumas linhas de tabelas num banco de dados num objeto na memorio em estado valido nao eh regra de dominio, mas funcao propria da camada de persistencia.

A

Ok Sérgio, porém você há de concordar comigo que nem tudo é Aggregate. Muitas vezes uma entidade é genuinamente completa.

Bom, acho que nos entendemos.

YvGa, os DAOs podem devolver objetos de domínio (mas tendo como cliente o repositório), desde que estes não sofram transformações como no caso de Aggregates.

S

A) Repository como um Mapper

Faltou esclarecer este ponto. No codigo de exemplo que dei, o Repositorio está mapeando dados brutos do DAO para objetos Entity. É por isso que ele é um mapper. Quando for dado um store(Conta) o repositorio tem que obter os dados brutos a partir do entity e passar ao DAO.

B) Dever usar Hibernate no Repositorio

Eu nunca disse que devia fazer isso. Eu disse que podia. Aliás todo o meu argumento é que vc deve ter um DAO totalmente separado das reponsabildiades do repositorio. Mas isso não é uma condição sin qua non para usar DDD.
Pode , pode , usar a api de persistencia directamente no repositorio. Acho que concordados que isso é ruim, embora possivel e viável em sistemas que não precisa usar outra coisa que nao DB.
Eu defendo exatamente o total desacoplamento do DAO do dominio e o uso do Repositorio de DAO como estratégias para acesar aos dados (padrão Strategy).

C) Qual a necessidade de ter DAO entao?

Poder mudar a persistência a gosto. Sem mudar uma virgula ( um ponto-e-virgula) do codigo do dominio. Aliás, mudando apenas um properties qq.

D)Os DAOs nao devem conhecer os objetos de dominio

Se os DAOs conhecem os objetos eles não podem ser usados noutro domínio contrariando o objetivo descrito em C. Um DAO cuja assinatura seja Cliente find(id) não é universal. Obrigaria a definir uma interface DAO para cada Entity o que é absurdo. Bastaria T find(Class entityclass, Object key). Que à primeira vista não viola nada.
Eu posso até viver com isso. O problema não é esse. São os metadados.

A camada de cima pode conhecer a de baixo, mas não ao contrario. A persistencia não pode conhecer o dominio.
O Hibernate não conhece o dominio já que usa reflection. Mas um dao do tipo Cliente find(id) depende do dominio. Contudo o hibernate precisa ser informado do meta-modelo do dominio (as relações)

E) nem tudo é Aggregate

Certo. Para esses casos vc usa um Repositorio padrão com operações padrão do tipo “pega dados ,cria objeto, insere dados no objeto, retorna objeto”. Se o DAO já faz isso, ok, mas se uma implementação do DAO faz isso , TODAS as outras terão que fazer tb. Cria-se um contrato implicito no DAO que não é a sua responsabilidade.
Se é um agregate, sobreescrevam-se os metodos corretos do repositorio generico e pronto.

O que não faz sentido:
Ter um repositorio que é delegador para o DAO porque o DAO faz-tudo.
Ter um repositorio que faz tudo e um o DAO que não faz nada sem ser delegar para outra API.

A

DataMapper é uma extensão de Mapper. O respositório pode até assumir um papel de Mapper ao modelar um Aggregate, mas de um DataMapper seria bizarrise.

No mais é isso mesmo Sérgio, concordo com os pontos colocados. Não é errado ter uma interface DAO para cada entidade de ORM, mas também não acho que isso tenha um cheiro bom. Em minhas implementações, utilizo BaseDao, fornecendo implementações dos gerenciadores das entidades deixando isso transparente ao repositório.

A

color=oliveembora, contudo, porém, pra ser sincero,(…) o DataMapper pode resolver na maioria dos casos Aggregates, pelo menos nos casos onde trabalhei.[/color]” :twisted:

Criado 15 de setembro de 2007
Ultima resposta 17 de out. de 2007
Respostas 58
Participantes 8