Refatoração de Camadas para utilização DTO + CDI e Rich Domain Model

Estou desenvolvendo um projeto core que será utilizado em vários outros projetos. Será gerado um .jar dele etc…
Este core, deve prover a base para persistência, e ainda alguns modelos de domínio que são comuns a todas aplicações que vão utilizar ele.
Vou tratar aqui da básica: Cadastro de Pessoa.
Atualmente estou trabalhando com as seguintes “camadas” e classes:
No core:
1 - DAO: Contém a classe AbstractHibernateDAO onde é feito todas as operações que serão usadas pelos Repository.
2 - Repository: Contém a classe AbstractRepository com os cruds extendendo de AbstractHibernateDAO, e no caso PessoaRepositroy que obviamente extende AbstractRepository, em PessoaRepository contém por exemplo o método buscarPessoasPorMunicipio(Municipio municipio).
3 - Model : contém as Entity por exemplo Pessoa, Endereco, Municipio. (estes models atualmente estão servindo apenas para mapeamento O/R “anêmicos”)
4 - Service: classe PessoaService, básicamente só faz chamadas aos Repository. (sim não faz sentido ter, preciso mudar isso, minha intenção era que os outros projetos não acessem os Repository diretamente)

Nos projetos que usam o core:
Projeto desktop1:
1 - Models : seus models específicos,
2 - Repository : Implementação do AbstractRepository para seus models.
3 - Presenter: recebe o PessoaService (gostaria de usar eventos do cdi), e trabalha com ele entre a view. Manipula diretamente o model Pessoa. (gostaria de trabalhar com DTO e Wrapper)

Projeto Web1:
1 - Models : seus models específicos,
2 - Repository : Implementação do AbstractRepository para seus models.
3 - Controller: recebe o PessoaService (gostaria de usar eventos do cdi), e trabalha com ele entre a view. Manipula diretamente o model Pessoa. (gostaria de trabalhar com DTO e Wrapper)

Alguém consegue indicar um caminho para seguir com as alterações?

Segue as Classes Envolvidas:

core.model

class Pessoa{
     //estou omitindo os mapeamentos O/R
     Long id;
     String nome;
     //estes enderecos estão sendo inseridos ou atualizados por CASCADE, tenho que mudar isso principalmente no Update
     List<Endereco> enderecos;

     //getters e setters

     // TODO enriquecer esta entity para evitar o modelo Anêmico 
}
class Endereco{
     //estou omitindo os mapeamentos O/R
     Long id;
     Pessoa pessoa;
     Municipio municipio;
     String bairro;
     String rua;

     //getters e setters

     // TODO enriquecer esta entity para evitar o modelo Anêmico 
}
class Municipio{
     //estou omitindo os mapeamentos O/R
     Long id;
     String descricao;

     //getters e setters

     // TODO enriquecer esta entity para evitar o modelo Anêmico talvez aqui não tenha o que fazer
}

Repository

class PessoaRepository extends AbstractRepository<Pessoa, Long>{

   //métodos crud basicos estão na classe AbstractRepository
   public List<Pessoa> buscarPessoasPorMunicipio(Muncipio municpio){
         List<Pessoa> list = null;
         //query omitida
         return list;
   }
}
class EnderecoRepository extends AbstractRepository<Endereco, Long>{

   //métodos crud basicos estão na classe AbstractRepository
   // não tem nada específico, talvez Implementação desnecessária?
}
//esta classe se justifica para isolar os projetos core entre desktop1 e web1?
class PessoaSerivce{
       @Inject
       PessoaRepository  repository;
       // aqui talvez devesse receber um DTO Válido
      public cadastrar(Pessoa pessoa){
            try{
                repository.gravar(pessoa);
            } catch (Exception e){
                   throw new ExcessaoAoGravar(e);
            }
      }
}

Projeto Web1
Controller

class PessoaController{

      @Inject PessoaService service;

       @Get("/pessoa")
       public void form(){
       }
       @Consumes(value = { "application/json", "application/x-www-form-urlencoded" }) 
       @Post("/pessoa")
       //preciso trocar pra DTO pois pode vir por json e isso pode ser um problema pois por ex. usa Reflection para deserializar
       public void gravar(Pessoa pessoa){
             //preciso validar
             service.gravar(pessoa);
       }
}

Ter um core que sabe como persistir acho que tudo bem, o serviço ficaria acessível através de uma operação do domínio e não um cadastro.

Um core que provê persistência me parece estranho.

Vamos por partes.

b[/b] Eu não gosto de criar DAOs/Repositories genéricos com o intuito de “reaproveitar” os métodos. Isso, geralmente, acaba em código mal-feito. Por exemplo, suponha que você tem uma entidade de Países, você tem um PaisDAO aonde você implementa o select. Mas você não quer que tenha os métodos save, update e delete. Só que se você extender AbstractHibernateDAO, você vai ter esses métodos.
A solução mais elegante possível é você usar o Composite-pattern.
Você só pode usar herança quando você conseguir responder a pergunta: “É um?”.
Exemplos:
[list]PaisDAO é um AbstractHibernateDAO? Não, pois é de país, não de abstractHibernate…[/list]
[list]UserDAO é um AbstractHibernateDAO? Não, pois é de user, não de abstractHibernate…[/list]
A solução com o composite seria mais ou menos assim:

public UserDAO {

    private final HibernateGenericDAO&lt;User, Long&gt; delegate;

    public UserDAO (Session session) {
        this.delegate = new HibernateGenericDAO&lt;User, Long&gt;(session);
    }

    public User save(PersistentUser dto) {
        return delegate.save(dto);
    }

// Não quero os outros métodos. E está tudo bem na minha implementação.
}

Para isso funcionar, o seu AbstractHibernateDAO não pode ser abstract (por isso mudei o nome dele para HibernateGenericDAO)
O mesmo vale para o Repository, e qualquer outro tipo de classe que você for fazer. Se você, olhando para os nomes das classes, conseguir responder aquela pergunta, é por que você deve usar herança. Você vai ver que é raro usar herança.
Ah, muitas pessoas das antigas, por conta desse problema da herança, quando não queria que um método fosse invocado, sobrescrevia o método e implementava ele assim:

Ué, se eu não posso invocar, por que ele existe? A própria API do Java tem isso nas classes mais antigas… =S

b[/b] Modelos anêmicos. O nome é até que chique, mas para mim só quer dizer uma coisa: é uma classe que não tem comportamento.
Modelo anêmico, geralmente é uma classe que tem uma pancada de atributos e seus respectivos getters e setters. Getters e setters são os maiores inimigos. Getters costumam vazar encapsulamento, setter então, você perde controle do seu modelo.
Somente sobre isso tem bastante coisa na internet. Mas esse post é muito bom para entender um pouco (e olha que ele é de 2006!!!). Você deveria devorar esse blog de ponta a ponta, tem MUITA coisa lá, desde arquitetura até como otimizar o seu dia a dia.
Apenas listando um exemplo em que o getter pode ser problemático, teste essas classes:

public class MinhaEntidade {
    private List&lt;String&gt; nomes;

    public MinhaEntidade(List&lt;String&gt; nomes) {
        this.nomes = new ArrayList&lt;String&gt;(nomes); // Criando uma cópia.
    }

    public List&lt;String&gt; getNomes() {
        return nomes;
    }
}
public static void main(String args[]) {
    MinhaEntidade obj = new MinhaEntidade(Arrays.asList("a", "b", "c"));

    List&lt;String&gt; nomes = obj.getNomes();
    System.out.println(nomes); // [a, b, c]
    nomes.clear()
    nomes.add("outro nome");

    System.out.println(nomes); // [outro nome]
    System.out.println(obj.getNomes()); // [outro nome]
}

** Vou continuar mais tarde, está ficando muito grande **

O core Produz a Configuration do hibernate, e dispara um evento.
O core Produz a SessionFactory com ApplicationScoped usando a Configuration injetada
O core Produz a Session também usando a SessionFactory injetada.
O core possui o AbstractRepository com operações de crud e FindBuilder pra criar queries de maneira fluente.

Assim os projetos que usam ele precisam:
ter o hibernate.cfg no seu resources com os parametros da conexão.
Observar o evento de configuração e adicionam as Entity específicas.
E aí só criar os Repository extendendo AbstractRepository

Isso que eu quis dizer com Prover base para persistência

A princípio está funcionando certo essa parte.

do jeito sendo montado vejo uma chance alta de ter muito boilerplate e baixa produtividade tambem

-service repository e dao os tres achei muito overkill. A nao ser que haja uma razao muito forte, isso vai atrapalhar bem mais que ajudar

-service nao deve ser por entidade, mas por assunto. entidade mais fraca cabe perfeitamente em service referente ao assunto que a contem

-dto e wrapper nao usar via de regra mas apenas em casos pontuais e se houver necessidade. seu model nao ser anemico diminuiria dependencia destes tipos de classe

[quote=Rafael Guerreiro]Vamos por partes.

b[/b] Eu não gosto de criar DAOs/Repositories genéricos com o intuito de “reaproveitar” os métodos. Isso, geralmente, acaba em código mal-feito. Por exemplo, suponha que você tem uma entidade de Países, você tem um PaisDAO aonde você implementa o select. Mas você não quer que tenha os métodos save, update e delete. Só que se você extender AbstractHibernateDAO, você vai ter esses métodos.
A solução mais elegante possível é você usar o Composite-pattern.
Você só pode usar herança quando você conseguir responder a pergunta: “É um?”.
[/quote]

Concordo em partes, eu vejo muito menos entidades que não podem ter Save, Update ou Delete do que as que podem.
E se você chamou o método Save é porque ele precisa ser implementado. No caso do “generic” ele já estaria. Se você não quer que o País seja excluído, por que tem o Botão excluir na tela do usuário, chama o excluir no controller, e chama o excluir no DAO?

Entendi seu ponto de vista mas acho que no caso específico do DAO o composit não me parece muito útil.

Algumas coisas para você ler:

Do blog da caelum, sobre esse e alguns outros conceitos de design.
http://blog.caelum.com.br/como-nao-aprender-orientacao-a-objetos-heranca/
http://blog.caelum.com.br/effective-java-segunda-edicao/
http://blog.caelum.com.br/possibilidades-de-design-no-uso-do-seu-generic-dao/
http://blog.caelum.com.br/principios-do-codigo-solido-na-orientacao-a-objetos/

http://stackoverflow.com/questions/38820/which-class-design-is-better
http://stackoverflow.com/questions/49002/prefer-composition-over-inheritance?answertab=votes#tab-top

[quote=andersonscherrer]O core Produz a Configuration do hibernate, e dispara um evento.
O core Produz a SessionFactory com ApplicationScoped usando a Configuration injetada
O core Produz a Session também usando a SessionFactory injetada.
O core possui o AbstractRepository com operações de crud e FindBuilder pra criar queries de maneira fluente.

Assim os projetos que usam ele precisam:
ter o hibernate.cfg no seu resources com os parametros da conexão.
Observar o evento de configuração e adicionam as Entity específicas.
E aí só criar os Repository extendendo AbstractRepository

Isso que eu quis dizer com Prover base para persistência[/quote]

tudo importante do ponto de vista da implementação, mas não é essencial (core) do ponto de vista do negócio.

ícaro, ele quer usar DTO pra comunicar com o cliente em outro local, e não apenas trafegar dados entre camadas, ou seja, ele quer criar seu próprio protocolo RPC binário para computação distribuída, usando uma interface baseada em json!

[quote=pfk66]ícaro, ele quer usar DTO pra comunicar com o cliente em outro local, e não apenas trafegar dados entre camadas, ou seja, ele quer criar seu próprio protocolo RPC binário para computação distribuída, usando uma interface baseada em json!
[/quote]

Basicamente isso, estou usando o VRaptor e o Gson para receber e deserializar o JSON, assim se eu deixar o objeto Pessoa direto, eu não tenho controle do que pode chegar no meu JSON pois o Gson utiliza reflection para deserializar. Assim eu posso usar um DTO como o Rafael sugeriu. E depois em algum ponto criar a entity Pessoa para persistir ou qualquer outra coisa.

[quote=Rafael Guerreiro]Algumas coisas para você ler:

Do blog da caelum, sobre esse e alguns outros conceitos de design.
http://blog.caelum.com.br/como-nao-aprender-orientacao-a-objetos-heranca/
http://blog.caelum.com.br/effective-java-segunda-edicao/
http://blog.caelum.com.br/possibilidades-de-design-no-uso-do-seu-generic-dao/
http://blog.caelum.com.br/principios-do-codigo-solido-na-orientacao-a-objetos/

http://stackoverflow.com/questions/38820/which-class-design-is-better
http://stackoverflow.com/questions/49002/prefer-composition-over-inheritance?answertab=votes#tab-top[/quote]

Ótimos posts, confesso que tento seguir mas as vezes não vai, como é o caso que estou publicando este Tópico e pedindo auxílio.

Ainda vou tentar justificar o uso de Abstract e herança no DAO:
1 - GenericDAO é uma implementação genérica de operações que eu considero essenciais em um DAO;
2 - PessoaDAO é um DAO, assim como GenericDAO, mas contém operações Específicas como: buscaPessoasInativas();
3 - Não acho que determinar se uma entidade vai poder ser Persistida, Alterada ou Excluida é papel do DAO, mas sim da Regra de Negócio.
Exemplo:
Projeto do João: utiliza o meucore.jar como dependencia. João pede pra que se a pessoa foi cadastrado errado ele possa ser excluído.
Projeto do Zé: utiliza o meucore.jar como dependencia. Zé pede pra que se a pessoa foi cadastrado errado ele não seja excluído (se quiser te passo um software muito popular que é assim).
O que vou fazer se o PessoaDAO não tem um método excluir?
É mais fácil e mais coerente eu ir no PessoaController do Zé e simplesmente não ter o método excluir, já que essa é uma Regra de Negócio do Zé.

[quote=andersonscherrer][quote=Rafael Guerreiro]Algumas coisas para você ler:

Do blog da caelum, sobre esse e alguns outros conceitos de design.
http://blog.caelum.com.br/como-nao-aprender-orientacao-a-objetos-heranca/
http://blog.caelum.com.br/effective-java-segunda-edicao/
http://blog.caelum.com.br/possibilidades-de-design-no-uso-do-seu-generic-dao/
http://blog.caelum.com.br/principios-do-codigo-solido-na-orientacao-a-objetos/

http://stackoverflow.com/questions/38820/which-class-design-is-better
http://stackoverflow.com/questions/49002/prefer-composition-over-inheritance?answertab=votes#tab-top[/quote]

Ótimos posts, confesso que tento seguir mas as vezes não vai, como é o caso que estou publicando este Tópico e pedindo auxílio.

Ainda vou tentar justificar o uso de Abstract e herança no DAO:
1 - GenericDAO é uma implementação genérica de operações que eu considero essenciais em um DAO;
2 - PessoaDAO é um DAO, assim como GenericDAO, mas contém operações Específicas como: buscaPessoasInativas();
3 - Não acho que determinar se uma entidade vai poder ser Persistida, Alterada ou Excluida é papel do DAO, mas sim da Regra de Negócio.
Exemplo:
Projeto do João: utiliza o meucore.jar como dependencia. João pede pra que se a pessoa foi cadastrado errado ele possa ser excluído.
Projeto do Zé: utiliza o meucore.jar como dependencia. Zé pede pra que se a pessoa foi cadastrado errado ele não seja excluído (se quiser te passo um software muito popular que é assim).
O que vou fazer se o PessoaDAO não tem um método excluir?
É mais fácil e mais coerente eu ir no PessoaController do Zé e simplesmente não ter o método excluir, já que essa é uma Regra de Negócio do Zé.[/quote]

sobre 3, toda entidade pode ser persistida. É pra isso que elas existem.

Bom, se vocês acham que isso é uma regra, entendo que vocês não trabalham com banco de dados complexos, com DBAs, com vários Schemas e até vários bancos numa mesma aplicação.

Garanto para vocês: existem entidades que não podem ser persistida, vai dar erro no banco de dados pois o seu usuário que está conectando não tem privilégio de inserção. (por determinação do DBA, por exemplo)

Segundo, o design das classes não se deve limitar ao “basta não invocar tal método”. Estamos falando de design, não de praticidade.

Justificar a herança preguiçosa não é uma saída interessante aos olhos do design.

Com a herança, você tem altíssimo acoplamento, métodos indesejados e outros problemas. É preciso ter certeza de que quer usar herança.

Voce pode comentar um pouco de como sera feito o deploy dessas aplicacoes?

Nao entendi se as aplicacoes sera auto suficientes, acessando diretamente o banco de dados através das classes do core ou se o core será exposto como um servico web e entao as aplicacoes consumirao isso através de uma API.

[quote=fabiofalci]Voce pode comentar um pouco de como sera feito o deploy dessas aplicacoes?

Nao entendi se as aplicacoes sera auto suficientes, acessando diretamente o banco de dados através das classes do core ou se o core será exposto como um servico web e entao as aplicacoes consumirao isso através de uma API.
[/quote]

As aplicações criam o hibernate.cfg no classpath e o core somente carrega as configurações, seta algumas propriedades como hibernate envers, e cria a sessionfactory, e fornece a entidade HibernateDAO por exemplo para que as aplicações utilizem ela com compoisite ou com herança para criar seus DAO específicos.

Bom, se vocês acham que isso é uma regra, entendo que vocês não trabalham com banco de dados complexos, com DBAs, com vários Schemas e até vários bancos numa mesma aplicação.

Garanto para vocês: existem entidades que não podem ser persistida, vai dar erro no banco de dados pois o seu usuário que está conectando não tem privilégio de inserção. (por determinação do DBA, por exemplo)

Segundo, o design das classes não se deve limitar ao “basta não invocar tal método”. Estamos falando de design, não de praticidade.

Justificar a herança preguiçosa não é uma saída interessante aos olhos do design.

Com a herança, você tem altíssimo acoplamento, métodos indesejados e outros problemas. É preciso ter certeza de que quer usar herança.[/quote]

Tranquilo, caso feito as alterações ficaria parecido com:

class PessoaRepository {  
  
   //Interface DAO
   private DAO&lt;Pessoa, Long&gt; dao;
    
    @Inject
   public PessoaRepository(Session session){
		dao = new HibernateDAO&lt;Pessoa, Long&gt;(Pessoa.class, session);
   }
   //métodos crud basicos agora são delegados para a implementação da interface DAO 
   //mas só vou delegar os que eu quero
   public void gravar(Pessoa pessoa){
        dao.gravar(pessoa);
   }

   public List&lt;Pessoa&gt; buscarPessoasPorMunicipio(Muncipio municpio){  
         List&lt;Pessoa&gt; list = null;  
         //query omitida  
         return list;  
   }  
}
class EnderecoRepository{  
  
   //Interface DAO
   private DAO&lt;Endereco, Long&gt; dao;
    
    @Inject
   public EnderecoRepository(Session session){
		dao = new HibernateDAO&lt;Endereco, Long&gt;(Endereco.class, session);
   }
   //métodos crud basicos agora são delegados para a implementação da interface DAO 
}

Seria isso?

[quote=andersonscherrer][quote=fabiofalci]Voce pode comentar um pouco de como sera feito o deploy dessas aplicacoes?

Nao entendi se as aplicacoes sera auto suficientes, acessando diretamente o banco de dados através das classes do core ou se o core será exposto como um servico web e entao as aplicacoes consumirao isso através de uma API.
[/quote]

As aplicações criam o hibernate.cfg no classpath e o core somente carrega as configurações, seta algumas propriedades como hibernate envers, e cria a sessionfactory, e fornece a entidade HibernateDAO por exemplo para que as aplicações utilizem ela com compoisite ou com herança para criar seus DAO específicos.[/quote]

Ao inves de exportar o core como um jar para acessar o banco de dados pq nao cria um webservice, um servico, que exportara as funcionalidades do core.
Entao as suas aplicacoes ultilizam isso como clientes, consumindo JSONs.

Assim nao se preocupando em distribuir JARs mas sim em definir uma boa API.

[quote]
Ao inves de exportar o core como um jar para acessar o banco de dados pq nao cria um webservice, um servico, que exportara as funcionalidades do core.
Entao as suas aplicacoes ultilizam isso como clientes, consumindo JSONs.

Assim nao se preocupando em distribuir JARs mas sim em definir uma boa API.[/quote]

Os outros projetos tem entidades específicas também, e consequentemente seus DAOs/Repository, e esses por sua vez utilizam a SessionFactory do core, entao ficaria meio complexo, além de aplicações desktop também utilizarem este core.

[quote=Rafael Guerreiro]
Bom, se vocês acham que isso é uma regra, entendo que vocês não trabalham com banco de dados complexos, com DBAs, com vários Schemas e até vários bancos numa mesma aplicação.

Garanto para vocês: existem entidades que não podem ser persistida, vai dar erro no banco de dados pois o seu usuário que está conectando não tem privilégio de inserção. (por determinação do DBA, por exemplo)

Segundo, o design das classes não se deve limitar ao “basta não invocar tal método”. Estamos falando de design, não de praticidade.

Justificar a herança preguiçosa não é uma saída interessante aos olhos do design.

Com a herança, você tem altíssimo acoplamento, métodos indesejados e outros problemas. É preciso ter certeza de que quer usar herança.[/quote]

Uma entidade é um fato ou informação que existe no banco de dados, então por definição ela precisa poder ser persistida. No caso das transações, quem executa é responsável por lidar com os casos onde eventualmente a transação falha. Isso geralmente é responsabilidade da camada de aplicação? Não vejo necessidade de projetar minhas entidades para estarem “cientes” disso.

Lembrando que, mesmo quando a transação falha em alguns casos o DBA pode querer saber as entidades que o usuário não autenticado tentou salvar.