DAO/Repositorio dentro ou fora do objeto?

Tenho o seguinte código no meu modelo de negócios:

userRepo.scoreUser(id, u.getId(), score);

// onde
// id = usuario que está recebendo a avaliaçao
// u.getId() = usuario que está dando a nota
// score = nota

Vale a pena optar pela arquitetura abaixo ou isso é trocar 6 por meia duzia?

User user = userRepo.loadById(id); // mete o repositorio dentro do Usuario que está sendo carregado e retornado, ou seja, loadById injeta o this dentro do user retornado!

user.scoreMe(u.getId(), score);

A implementação do metodo scoreMe é exatamente igual a primeira opção:

public void scoreMe(int user_id, int score) {
    userRepo.scoreUser(this.id, user_id, score);    
}

Na minha opinião:

Prós: o código fica mais orientado a objetos e menos procedural , sendo responsabilidade do usuário dar nota.

Contras: você teria que ter todos os seus objetos usuário com um repositório o que deve aumentar o overhead(uma lista de usuários), e se somente usuários carregados pelo loadById tivessem esse repositório, quebraria o modelo pois eu não poderia chamar scoreMe de qualquer usuário.

Obs: Acho que o método deveria ser score não? Pois o usuário atual da uma nota para outro usuário e não recebe a nota de outro usuário a ação é dar nota.

T+

O usuario passado como parametro é que dá nota para o usuário que está recebendo o método. Se fosse score() pareceria que é o contrário.

Estou a procura de uma boa justificativa para colocar o repository dentro do objeto, mas não encontro… Parece que fica mais bonito na teoria (concordo) mas na prática dá no mesmo no final das contas…

Usuário esta dando uma nota à outro usuário?

usuarioQueDaNota.score(nota, usuarioQueVaiReceberANota); //ou usuario.daNotaPara(nota, outroUsuario);
É o mais perto do pensamento humano que consigo chegar sem usar uma Fluent Interface.

E respondendo, dentro do objeto, numa camada mais abaixo, como de costume.

[quote=Bruno Laturner]Usuário esta dando uma nota à outro usuário?

[code]usuarioQueDaNota.score(nota, usuarioQueVaiReceberANota);
//ou

usuario.daNotaPara(nota, outroUsuario);
[/code]
É o mais perto do pensamento humano que consigo chegar.[/quote]

Assim fica mais intuitivo mesmo. No meu caso o objeto usuário está recebendo uma nota de outro usuário, por isso o scoreMe, ou seja, é o inverso. Poderia ser addScore.

Mas concordo que é melhor não ser contra-intuitivo e fazer do jeito que vc falou, é que o objeto que está dando a nota já está carregado e está na sessão. E o que está na sessão não faz sentido ter repositório nenhum.

Mas isso não tem nada haver com a dúvida do tópico. :roll:

Pergunta então: o user que está na sessão (usuário logado) deve ter o repository dentro? Acredito que não…

E o repositório, pode ter um pool de conexões lá dentro de forma que o repositorio possa ficar inativo durante um bom tempo até alguém chamar ele?

Hoje meu pool de conexões fica centralizado num único lugar e o container de IOC injeta as connections nos lugares que ela é necessária, como em um repositório. A questão é que o repositorio só vive enquanto sua connection viver, isto é, cada requisição web pega uma connection usa e devolve para o pool, como tem que ser. Se cada repositorio tiver uma referencia para o pool, então teremos o cenário de uma requisição abrindo várias conexoes diferentes, o que é péssimo.

Então dando rollback e voltando a minha pergunta original. Dentro ou fora e qual a real vantagem se for dentro?

Why not?

[quote=saoj]E o repositório, pode ter um pool de conexões lá dentro de forma que o repositorio possa ficar inativo durante um bom tempo até alguém chamar ele?

Hoje meu pool de conexões fica centralizado num único lugar e o container de IOC injeta as connections nos lugares que ela é necessária, como em um repositório. A questão é que o repositorio só vive enquanto sua connection viver, isto é, cada requisição web pega uma connection usa e devolve para o pool, como tem que ser. Se cada repositorio tiver uma referencia para o pool, então teremos o cenário de uma requisição abrindo várias conexoes diferentes, o que é péssimo.

Então dando rollback e voltando a minha pergunta original. Dentro ou fora e qual a real vantagem se for dentro além de ficar mais bonito?[/quote]

Bem, o pool deve ter o controle próprio dele de transações. Na maioria dos casos, cada requisição ao servidor gera uma thread para atendê-la. Num bom controle de conexões, somente uma conexão por thread é criada e utilizada, mais que isso é desperdício.

Considerando que há esse controle eficiente, então essa arquitetura é possível, e considero ser um ótimo exemplo de OO.

Respondendo a pergunta: Além de ficar mais bonito, a vantagem é que você deixará de mexer tanto com persistência, e passará a desenvolver somente para teu negócio.

Vide o primeiro exemplo, lá tinha repositório e negócio. No segundo “eliminamos” o repositório, agora só há negócio.

Se for repositório do DDD, que serve para guardar/reconstituir objetos, acho bem estranho o método se chamar scoreUser().

A única coisa que eu faria no exemplo do Bruno é inverter as ordem dos parâmetros para aumentar a expressividade (nesse caso, a fluência do código). Outros exemplos:

usuario.ehAvaliadoPor(outroUsuario, nota)
// ou
usuario.avalia(outroUsuario, nota)

Ao invés de injetar um repositorio em Usuario, vc tem a opção de criar um ‘domain service’ (DDD) como:

public class ServicoAvaliacaoImpl implements ServicoAvaliacao{
   public void usuarioEhAvaliadoPorOutroUsuario(Usuario usuario, Usuario outroUsuario, int nota){
      usuario.ehAvaliadoPor(outroUsuario, nota);
      repositorioUsuario.salva(usuario); // se não estiver usando um framework ORM 
   }
}

Ou, se preferir…

public class ServicoAvaliacaoImpl implements ServicoAvaliacao{
   public void usuarioEhAvaliadoPorOutroUsuario(Usuario avaliado, Usuario avaliador, int nota){
      avaliado.ehAvaliadoPor(avaliador, nota);
      repositorioUsuario.salva(avaliado); // se não estiver usando um framework ORM 
   }
}

Lembrando que esse service é de domain (domain layer), e, deve ser stateless. Assim, como sua construção é simples e facilmente gerenciado por um IoC container como o Spring (diferente de um Entity, por ex., ainda mais quando se tem um framework ORM envolvido), facilita que seus colaboradores sejam providos com facilidade, como é o caso dos repositórios.
Não confundir com o service layer do fowler.

Pra que repository? Normalmente o ‘usuário logado’ que fica na session não é o próprio ‘usuário’, já que são dois conceitos diferentes. ‘Usuario logado’ deveria ter apenas informações como identidade, permissões, nome completo, nome de usuário, e alguma outra coisa a mais que vale a pena ter deixar na session. Muitas vezes pode ser um ‘value object’ (!=DTO) criado durante a autenticação. Na realidade, ‘usuario logado’ e ‘usuario’ deveriam ser implementados em classes separadas, mas o que acontece na prática é que muita gente usa uma mesma classe para representar dois conceitos diferentes. Isso causa grandes problemas, já que os dois conceitos normalmente possuem diferentes comportamentos/estados, possuem invariantes diferentes, participam de aggregates (DDD) diferentes, etc. E, outra, como ficam os métodos toString(), hashCode(), equals() que não podem ser compartilhados entre os dois conceitos? É o mesmo problema daqueles que reutilizam a classe ‘Usuario’ para representar dados de login… a única coisa comum entre os dois talvez poderia ser o atributo ‘String usuarioLogin’, já que a própria senha tem semântica diferente nos dois conceitos (no Usuario provavelmente você tem uma hashSenha, e não exatamente senha). Resumindo, normalmente não faz sentido o ‘usuario logado’ ter acesso a um repository.

[quote=saoj][quote=Rapapel]
Obs: Acho que o método deveria ser score não? Pois o usuário atual da uma nota para outro usuário e não recebe a nota de outro usuário a ação é dar nota.
[/quote]
O usuario passado como parametro é que dá nota para o usuário que está recebendo o método. Se fosse score() pareceria que é o contrário.
[/quote]

public void scoreMe(int user_id, int score) {  
     userRepo.scoreUser(this.id, user_id, score);      
 }

disse com relação a esse método acima de da entidade Usuario, que acho que deveria ser usuarioAtual.daNotaPara(outroUsuario, nota) e não usuarioQueRecebeNota(usuarioQueDaNota, nota).

Boa sorte na pesquisa, se encontrar um bom motivo posta ai pra gente.

T+

Estou por fora de DDD, mas essa implementação de servico é bem parecida com o que eu fiz inicialmente, só que ao invés de usar ServicoAvaliação eu usei o repository mesmo, que nesse caso funciona mais como um DAO mesmo e tb é stateless.

servicoAvaliacao.usuarioEhAvaliadoPorOutroUsuario(avaliado, avaliador, nota);

userRepo.scoreUser(avaliado_id, avaliador_id, score);

Faz um certo sentido isso, mas lembrando que o usuário que está logado será utilizado a todo momento pelo sistema. Se eu guardo só o username ou o id na sessão, eu terei que a todo momento carregar/construir o usuário logado. Então porque não guardar o usuário (sem repositorio) na sessão?

O problema de colocar o repositorio dentro do objeto user é que vc terá que se policiar para nunca chamar um método que depende de um repositório de um usuário que não tem repositorio ou que o repositorio já está stale.

[quote=saoj]Tenho o seguinte código no meu modelo de negócios:

userRepo.scoreUser(id, u.getId(), score);

// onde
// id = usuario que está recebendo a avaliaçao
// u.getId() = usuario que está dando a nota
// score = nota

Vale a pena optar pela arquitetura abaixo ou isso é trocar 6 por meia duzia?
[/quote]

O seu objeto userRepo está atuando no sistema (to score é um verbo), portanto ele deveria estar em um objeto do tipo Serviço.
E o serviço deveria ser assim:

[code]public class ScoreService{

public void score(User userToScore, User userThatScored , Score score){
// codigo.
}
}[/code]

[quote=sergiotaborda][quote=saoj]Tenho o seguinte código no meu modelo de negócios:

userRepo.scoreUser(id, u.getId(), score);

// onde
// id = usuario que está recebendo a avaliaçao
// u.getId() = usuario que está dando a nota
// score = nota

Vale a pena optar pela arquitetura abaixo ou isso é trocar 6 por meia duzia?
[/quote]

O seu objeto userRepo está atuando no sistema (to score é um verbo), portanto ele deveria estar em um objeto do tipo Serviço.
E o serviço deveria ser assim:

[code]public class ScoreService{

public void score(User userToScore, User userThatScored , Score score){
// codigo.
}
}[/code]

[/quote]

Entendi. Eu estou fazendo assim, só que estou colocando tudo no DAO, pois essa modificacao é feita apenas no banco de dados, visto que estou trabalhando apenas com ids nesse caso e nao com os objetos em si.

Mas concordo que o mais certo é o ScoreServce chamar o dao, só que essa replicação de camadas ( action -> servico -> dao) me incomoda um pouco, apesar de na teoria ser a mais certa. Tenho desejos de transformar a minha action desacoplada do framework em serviço, evitando assim uma camada a mais. Seria a opção 4 daqui: http://blogs.mentaframework.org/posts/list/14.page

Isso foge um pouco do tópico, que é sobre colocar ou não o dao/repositorio dentro do objeto, mas é um assunto tb interessante. Colocar uma camada a mais, apenas para fazer uma chamada a outra (dao/repositorio) é bem correto na teoria, mas na prática é um pé-no-saco. Usando um framework loosely coupled acho que dá para, sem prejuízos, fazer a action ter o mesmo papel de serviço.

Mas neste caso você não estaria criando uma nova camada. O ScoreService neste caso é uma classe de domínio. É um pouco diferente destas fachadas que se tem usado por aí.

Então o seu programa não é orientado ao dominio e sim ao banco. Nesse caso o seu objeto userRep deveria se chamar userDAO.
Ao usar Repositorio vc está implicitamente dizendo que o sistema é orientado ao dominio e colocar ações no repositorio é automáticamente incoerente.

Uma action nunca será decoupled do framework. Basta que vc tenha que ler o ID do request para que isso seja verdade. E não me refiro ao objeto Request mas ao conceito request. Dito de outra forma, a sua action funcionaria em um programa swing ? Não ? Então ela está acoplada. O Serviço, por definição, não está acoplado. Se está acoplado não é um serviço.
O uso de uma camada de serviços tem dois objetivos : 1) encapsular as lógicas que alteram estado do sistema 2) tornar o dominio desacoplado da estrutura da aplicação, i.e. permitindo que ele seja usado em outro ambiente tecnológico.

A única forma de desacoplar o Serviço da infra é ter algum objeto que traduz a infra em objetos do dominio e os passa ao Serviço.
Então a sua action deveria pegar o ID do request, obter o usuário correspondente via Repositorio e passar ao Serviço já montado.

Se a sua aplicação é orientada a banco ignore o que eu falei, mas chame os objetos de DAO e não de Repositorio.
Neste caso tb não faz sentido colocar lógicas nos objetos e sim , ficariam no DAO.

Para coisas simples isto pode funcionam e pode até parecer elegante, mas não é. As poucas camadas significam que o sistema é amarrado.

[quote=sergiotaborda][quote=saoj]
Entendi. Eu estou fazendo assim, só que estou colocando tudo no DAO, pois essa modificacao é feita apenas no banco de dados, visto que estou trabalhando apenas com ids nesse caso e nao com os objetos em si.
[/quote]

Então o seu programa não é orientado ao dominio e sim ao banco. Nesse caso o seu objeto userRep deveria se chamar userDAO.
Ao usar Repositorio vc está implicitamente dizendo que o sistema é orientado ao dominio e colocar ações no repositorio é automáticamente incoerente.

Uma action nunca será decoupled do framework. Basta que vc tenha que ler o ID do request para que isso seja verdade. E não me refiro ao objeto Request mas ao conceito request. Dito de outra forma, a sua action funcionaria em um programa swing ? Não ? Então ela está acoplada. O Serviço, por definição, não está acoplado. Se está acoplado não é um serviço.
O uso de uma camada de serviços tem dois objetivos : 1) encapsular as lógicas que alteram estado do sistema 2) tornar o dominio desacoplado da estrutura da aplicação, i.e. permitindo que ele seja usado em outro ambiente tecnológico.

A única forma de desacoplar o Serviço da infra é ter algum objeto que traduz a infra em objetos do dominio e os passa ao Serviço.
Então a sua action deveria pegar o ID do request, obter o usuário correspondente via Repositorio e passar ao Serviço já montado.

Se a sua aplicação é orientada a banco ignore o que eu falei, mas chame os objetos de DAO e não de Repositorio.
Neste caso tb não faz sentido colocar lógicas nos objetos e sim , ficariam no DAO.

Para coisas simples isto pode funcionam e pode até parecer elegante, mas não é. As poucas camadas significam que o sistema é amarrado.
[/quote]

Obrigado. Dá mais trabalho fazer assim, principalmente quando a aplicação naturalmente possui a tendência de ser orientada a banco-de-dados (babá de DB). Mas o que vc falou é o mais correto…

O que faz ser um domain service não é o ‘userRepo’ estar atuando no sistema (afinal de contas, tudo está atuando no sistema), e, muito menos, o nome do método ser um verbo (deve ser um verbo!).
Afinal de contas, o que você quis dizer?

[quote=Gerson][quote=sergiotaborda]

O seu objeto userRepo está atuando no sistema (to score é um verbo), portanto ele deveria estar em um objeto do tipo Serviço.
E o serviço deveria ser assim:

[code]public class ScoreService{

public void score(User userToScore, User userThatScored , Score score){
// codigo.
}
}[/code]

[/quote]

O que faz ser um domain service não é o ‘userRepo’ estar atuando no sistema (afinal de contas, tudo está atuando no sistema), e, muito menos, o nome do método ser um verbo (deve ser um verbo!).
Afinal de contas, o que você quis dizer? [/quote]

Se o método contém um verbo isso significa que ele está executando uma ação que irá transformar o estado do sistema ( do sistema como um todo). Se não fosse assim, não seria uma ação e não precisaria de um verbo. O ponto é: se é um verbo => é uma ação no sistema.

Ações no sistema tevem estar contidas em Serviços. Isto porque em objetos de entidade só faz sentido colocar métodos que alterem o estado dos próprios objetos e não do sistema como um todo.

Repositorios não causam alterações do sistema, eles são passivos. São localizadores de dados. Claro que se o seu Repositorio tem métodos CRUD , vc poderá pensar que ele está alterando o sistema, mas quem está alterando de facto é quem manda executar esses comandos.

O ponto era: Se o seu objeto contém logica de negocio ele ou é uma entidade ou é um serviço. Nunca será um repositório.
A escolha caberia então entre colocar o método num Serviço ou na entidade usuário. Porque o método não altera o usuário, é mais claro que seja um serviço a fazer isso.

Depende… essa “ação” que você diz pode ser de um Entity ou de um Value Object, também. Não obrigatoriamente, essa operação deve ser colocada em um service, seja este de domain, de application, de insfrastructure, ou da camada que for. Um domain service, por ex., deve ser usado para representar (e explicitar) uma operação que não tem uma relação natural com um Entity ou um Value Object.

Bom, não entendi bem o que você quer dizer com “Sistema como um todo”, mas, de qualquer forma, se o seu domínio usa um modelo de objetos (Domain Model - PEAA) poderiamos ter:

joao.compra(carro); // num relacionamento bidirecional, o carro deve saber que o joao é o próprio dono.
joao.casaCom(maria); // a mesma coisa acontece aqui, onde a maria sabe que se casou com joao.
joao.ehAvaliadoPor(maria, 10); // joao conhece suas avaliacoes, e, agora, recebeu mais uma de maria.

Naturalmente, se necessário, poderia também ter um domain service quando a lógica de avaliação é importante e complexa, como:

public ServicoAvaliacaoImpl implements ServicoAvaliacao{
  private RepositorioUsuario repositorioUsuario = ...;
  public void usuarioEhAvaliadoPorOutroUsuario(Usuario usuario, Usuario outroUsuario, int nota){
    usuario.ehAvaliadoPor(outroUsuario, nota);
    //... mais lógica envolvida quando um usuario é avaliado por outro usuário
  }
}

E uma possível implementação para Usuario poderia ser…

class Usuario{

   private List<Avaliacao> avaliacoes = ...;
  
   public void ehAvaliadoPor(Usuario outroUsuario, int nota){
      adicionaNovaAvaliacaoDe(outroUsuario, nota);
   }
  
   private void adicionaNovaAvaliacaoDe(Usuario outroUsuario, int nota){
      this.avaliacoes.add(new Avaliacao(nota, outroUsuario));  
   }

   ...

}

Neste caso, podemos assumir que Usuario e Avaliacao fazem parte do mesmo aggregate. O root dele, naturalmente, é o Usuario.

Um Value Object, no domínio, pode conter lógica de negócio também.

Bom, não é simplesmente porque não ‘altera’ o próprio objeto que deve ficar no Service. Acho que é uma regra muito simplória.

[quote=Gerson][quote=sergiotaborda]
Ações no sistema tevem estar contidas em Serviços.
[/quote]

Depende… essa “ação” que você diz pode ser de um Entity ou de um Value Object, também. Não obrigatoriamente, essa operação deve ser colocada em um service, seja este de domain, de application, de insfrastructure, ou da camada que for.
[/quote]

Ou você não leu e entendeu o que escrevi antes ou vc está fingindo que não entendeu. Eu já disse que poderia ser em um entity.
Agora, no Value Object vc já está forçando a barra. O BigDecimal é um Value Object. Ele não contem nenhuma ação sobre o sistema. Um Value Objeto, por definição, só contém acções sobre si mesmo (add, multiply, etc… são acções sobre o proprio objeto)

Exato. É esse o caso que se aplica porque pontuar o usuário é uma ação de outro usuário , portanto não é ação de nenhum deles.

Essa é quanto a mim uma noção ingénua de DDD. Na prática nenhum sistema funcionaria assim.
A razão é simples : a ação entre as entidades é uma entidade em si mesma. Então quando o joao casa com a maria um objeto casamento será criado , e ele é que contém as ações sobre o sistema.

No exemplo, quando o usuário pontua outro é criado um objeto pontuação que é a associação dos usuários com a nota.
O codigo abaixo é válido


Pontuacao pont = usuário.pontua(outrousuario, nota);

Repositiorio.de(Pontuação).save(pont); 

o codigo a seguri é gamb


usuário.pontua(outrousuario, nota);


// usuário 

public class Usuario {

   public void pontua ( Usuario outro, int nota){
          usuarioDao.pontua(this.id, outro.id, nota);
  }

}

Não , não pode. Ele contém logica relativa a ele. VO não invocam serviços nem repositorios nem coisa nenhuma. eles são invocados e usados por esses outros objeto. O método que eles têm reference ao estado que eles detêm. Só isso. Não ao estado do sistema.

Vc acha o que vc quizer. O ponto é que essa é a regra para saber se algo fica na entidade ou em um serviço.

[quote=sergiotaborda]O codigo abaixo é válido

Bacana isso aqui. Poderia ficar ainda mais bonitinho:

:XD:

Você leu alguma literatura sobre DDD mesmo?

Pelo amor de deus, defina o que são “ações sobre si mesmo”! Bom, tomando como exemplo o BigDecimal que você citou, e considerando que, obviamente, você deve saber que ele é imutável, o que você quer dizer com isso?
“ação sobre o sistema” é algo que você está inventando. Não sabe o que dizer, e, por isso, de forma generalizada, diz simplesmente “ação sobre o sistema” e “ação sobre si mesmo”. Essa sua classificação entre “ações sobre o sistema” e “ações sobre si mesmo” foi você que inventou? Defina-o… é muito fácil decidir usar isso como um critério só porque você quer. Aliás, um entity “tem uma ação sobre si mesmo” (usando seu termo vago), então o sistema não é alterado? Confuso, não acha?

Bom, quando você definir melhor o que inventou, aí podemos conversar sobre isso.

Bom, eu uso e funciona. É só saber usar direito. Estude Domain Model (PEAA).

Bom, novamente, defina primeiro “ações sobre o sistema”. Do contrário, fica dificil discutir.

É obvio que ele contém lógica relativa a ele, a não ser que você não saiba o que é coesão.
E, novamente, VOs do domain layer, inclusive, contém lógica de domínio sim. A não ser que você tenha criado uma outra definição para ‘lógica de domino’ (ou lógica de negócio).