Paginação e Ordenação em DDD

Olá pessoal,

Estou estudando o livro sobre DDD e estou achando muito interessante os conceitos apresentados, mas estou com dificuldades para entender algumas coisas, como pode ser implementada a paginação de resultados de serviços que exigem um processamento de muitas instancias de classes? e ordenação?

Como vocês tem implementado estes dois conceitos tanto para a tela como para o domínio?

Obrigado.

Os métodos dos repositórios podem receber parâmetros indicando o offset, o máximo e talvez até a ordenação.

Se pensarmos no repositório como uma implementação de collections, podemos concluir que é de responsabilidade dele, o repositório, cuidar da organização, retorno e ciclo de vida dos dados.

A implementação prática depende muito da estratégia da sua paginação. Mas se for 1 query para cada página por exemplo, supondo que estamos falando de DBs relacionais, eu repassaria esses parâmetros para os DAOs. Mas há casos e mais casos. =)

Eu já acho que ordenação e paginação não é problema do repositório. Depois de muito quebrar a cabeça decidi mandar o DAO e o Repositório pra cucuia. Como opção, comecei criando as queries usando session diretamente. Depois refatorei, encapsulando as queries dentro de query objects.

Acho muito ruim implementar paginacao usando DAO ou Repositório pois eu preciso de no mínimo dois métodos:

Repositorio.findAll(int first, int count);
Repositorio.findAllCount();

Outro problema que tive é que usando o banco de dados hsqdb (pelo menos) eu não consigo utilizar a mesma query para fazer a busca e o count. Isso pq quando adiciona ordenação na minha criteria o count simplesmente quebra. Ai, lá vai gambi pra organizar tudinho.

enfim… resumindo:

[code]public interface Query extends Serializable {

T findFirst();

Collection<T> findAll();

Collection<T> findAll(int first, int count);

int count();

}[/code]

[code]public abstract class DefaultHibQuery implements Query {

@Dependency
private SessionFactory sessionFactory;

protected abstract DetachedCriteria buildDetachedCriteria();

protected Order[] ordering() {
	return new Order[] {};
}

public int count() {
	DetachedCriteria detached = buildDetachedCriteria();
	Criteria crit = toCriteria(detached, false);
	crit.setProjection(Projections.rowCount());
	
	return (Integer) crit.uniqueResult();
}

@SuppressWarnings("unchecked")
public Collection<T> findAll() {
	DetachedCriteria detached = buildDetachedCriteria();
	Criteria crit = toCriteria(detached, true);
	return crit.list();
}

@SuppressWarnings("unchecked")
public Collection<T> findAll(int first, int count) {
	DetachedCriteria detached = buildDetachedCriteria();
	Criteria crit = toCriteria(detached, true);
	crit.setFirstResult(first);
	crit.setMaxResults(count);
	return crit.list();
}

@SuppressWarnings("unchecked")
public T findFirst() {
	DetachedCriteria detached = buildDetachedCriteria();
	Criteria crit = toCriteria(detached, true);
	crit.setMaxResults(1);
	return (T) crit.uniqueResult();
}

protected Criteria toCriteria(DetachedCriteria detached, boolean applyOrdering) {
	Session sess = sessionFactory.getCurrentSession();
	Criteria crit = detached.getExecutableCriteria(sess);
	
	if (applyOrdering) {
		for (Order order : ordering()) {
			crit.addOrder(order);
		}
	}
	
	return crit;
}

}[/code]

[code]public class QueryUserByLogin extends DefaultHibQuery {

private String userLogin;

public QueryUserByLogin(String userLogin) {
	this.userLogin = userLogin;
}

@Override
protected DetachedCriteria buildDetachedCriteria() {
	DetachedCriteria detached = DetachedCriteria.forClass(User.class);
	detached.add(Restrictions.eq("login", userLogin));
	return detached;
}

}[/code]

[quote=euprogramador]Olá pessoal,

Estou estudando o livro sobre DDD e estou achando muito interessante os conceitos apresentados, mas estou com dificuldades para entender algumas coisas, como pode ser implementada a paginação de resultados de serviços que exigem um processamento de muitas instancias de classes? e ordenação?

Como vocês tem implementado estes dois conceitos tanto para a tela como para o domínio?

Obrigado.[/quote]

Eu uso um objeto de paginação (algo nas linhas disto) mas o segredo aqui é objeto Query que não é um conjunto de objetos mas um Fastlane para refazer a pesquisa várias vezes.

Moral da historia, vc resolve com bom OO. DDD não lhe vai ensinar isso.

[quote=Thiago Senna]Eu já acho que ordenação e paginação não é problema do repositório. Depois de muito quebrar a cabeça decidi mandar o DAO e o Repositório pra cucuia. Como opção, comecei criando as queries usando session diretamente. Depois refatorei, encapsulando as queries dentro de query objects.

Acho muito ruim implementar paginacao usando DAO ou Repositório pois eu preciso de no mínimo dois métodos:

Repositorio.findAll(int first, int count);
Repositorio.findAllCount();

Outro problema que tive é que usando o banco de dados hsqdb (pelo menos) eu não consigo utilizar a mesma query para fazer a busca e o count. Isso pq quando adiciona ordenação na minha criteria o count simplesmente quebra. Ai, lá vai gambi pra organizar tudinho.

enfim… resumindo:
[/quote]

Ótimo, agora pega o seu objeto Query e chama ele de dentro do repository. \o/

A implementação não precisa ficar no repository. Porém se você excluir o repository e começar a criar métodos alternativos para manipular os dados, estará ferindo os principios do DDD. Como o Sergio falou, você resolve isso com bom OO e o DDD vai funcionar normalmente como em qualquer outro caso.

Pra que?

Sim, de fato!

Mas que dados alternativos são esses que só seria interessante manipular dentro de um repositório?

Pode ser, mas dá muito trabalho. Usar query object já é bem elgante se falando de OO. Quanto a DDD, manter os repositórios se transformou em uma guerra contra os frameworks que utilizo.

Pra que?
[/quote]
Bom, os benefícios do uso de repositório são vários. O Eric Evans cita que sem eles o risco é que o domain fique “limboso” (de limbo mesmo).
Entendo que o caso não seja muito óbvio se levarmos em consideração somente o caso da paginação, mas estamos falando de um sistema maior, certo?

Mas que dados alternativos são esses que só seria interessante manipular dentro de um repositório?[/quote]
Não dados alternativos, eu disse métodos alternativos ou formas alternativas. Por exemplo obter os dados por fora, sem o repositorio em algumas vezes e em outras obter através do repositorio. O ponto de acesso a dados deve ser único. Para isso servem os repositórios.

Pode ser, mas dá muito trabalho. Usar query object já é bem elgante se falando de OO. Quanto a DDD, manter os repositórios se transformou em uma guerra contra os frameworks que utilizo.[/quote]
Bom, dois pontos… primeiro, QueryObject é uma solução inteligente de OO. Pode ser utilizada em conjunto com os repositórios.

Quanto ao caso dos frameworks, eu concordo que às vezes é difícil de manter. Em casos como o Rails/Grails por exemplo, é uma opção ignorar o uso de repositórios, mas aproveitar outros conceitos do DDD. Eu faço isso frequentemente.
Para modelos mais complexos ou maiores, mesmo com esses frameworks, eu aconselho o uso dos repositorios. Talvez a dificuldade de uso possa se revelar uma má separação das camadas? Talvez. Eu investigaria.

Humm, acredito que o tamanho do projeto não é o problema. A quantidade e a qualidade dos desenvolvedores poderiam afetar se a arquitetura será mais “burocrática” ou mais “froxa”.

Então, no meu caso afroxei. No caso de obter os dados por fora será sempre através de QueryObjects. Uma operação mais complexa envolvendo mais entidades coloca-se em um Service. Outra operações como load, save e remove tenho abstraído nos próprios componentes de UI.

Eu tenho buscado dia a dia uma produtividade similar a Rails e Grails, no entanto, utilizando Java, Spring, Hibernate e Wicket. Num cenário como este manter o repositório se tornou uma guerra contra esses frameworks, mesmo. Isso se agravou principalmente por causa do Wicket, já que não é tão complicado criar componentes de UI um pouco mais espertos.

Sinceramente… tenho achado que em geral, nós desenvolvedores java, criamos camadas demais. Outros frameworks e linguagens tem se saído tão bem com arquiteturas tão mais simples. Então, por enquanto, vou tentar seguir esta receita. O mínimo de camadas possíveis. Caso necessário, no decorrer do projeto, refatoramos para algo mais “defensivo”.

Na medida do possível, a idéia é manter e aplicar as boas idéias do DDD, só que inicialmente sem o repositório, que é onde a briga contra os frameworks é mais intensa. :slight_smile:

[quote=Thiago Senna]

Pode ser, mas dá muito trabalho. Usar query object já é bem elgante se falando de OO. Quanto a DDD, manter os repositórios se transformou em uma guerra contra os frameworks que utilizo.[/quote]

QueryObject é um padrão , mas como todos os padrões pode ser usado da forma errada. QueryObject precisa ser usado conjuntamente com um Interpreter. No seu caso o objeto é QO e Interpreter ao mesmo tempo :shock: portanto violando o
principio de responsabilidade e portanto não sendo OO elegante ( esquece elegante, não é bom OO para começo de conversa)

O problema de não usar QueryObject conjuntamente (cooperação) com um outro objeto - neste caso o repositorio que faria o papel
de interpreter - é o acoplamento. Com um QO desassoiado do repistorio vc pode ter muito mais flexibilidade porque pode mandar o mesmo QO para reposiotorios com implementações diferentes (um real e um de teste, por exemplo).

Leal Thiago, parece que estamos entrando em consenso agora. =)

[quote=Thiago Senna]

Eu tenho buscado dia a dia uma produtividade similar a Rails e Grails, no entanto, utilizando Java, Spring, Hibernate e Wicket. Num cenário como este manter o repositório se tornou uma guerra contra esses frameworks, mesmo. Isso se agravou principalmente por causa do Wicket, já que não é tão complicado criar componentes de UI um pouco mais espertos.[/quote]
Só reparei que talvez sua dificuldade para usar os respositorios esteja exatamente na aplicaçào do smart-UI ou componentes UI mais espertos. Não digo que é certo ou errado, mas muitos, inclusive o Eric Evans menciona no livro, o pattern Smart-UI exclui mutuamente o uso do MDD, permitindo o uso das outras pŕaticas do DDD.

Esse é o espírito. =)

Entendo, entendo. Mas em minha opinião este esforço não tem valido a pena. Nada contra em implementar um bom OO, mas a real é que no fim, se vc juntar tudo que você implementou para fazer tudo ficar bonitinho praticamente vira um framework novo. Não é a toa que você está desenvolvendo o MiddleHeaven, certo?

Infelizmente aplicar patterns, OO, separação de camadas e essas coisas de forma bonitinha é sofrido demais. Principalmente hoje dependendo de qual framework vc está adotando.

Humm… deixe eu levantar uma questão interessante baseado em uma colação que você fez:

Hoje, utilizando wicket, ainda posso colocar o interpreter fora do repositorio. Eu poderia numa boa criar um componente de UI que fizesse a interpretação do QueryObject. Melhor fazer isso do que criar o componente de UI, que chama o repositório e que por fim, chama o QueryObject. IMHO é muita delegação. A maioria dos projetos não precisam ser defensivos a este ponto, acho. E vocês, o que acham?

[quote=feliperod]Leal Thiago, parece que estamos entrando em consenso agora. =)

[quote=Thiago Senna]

Eu tenho buscado dia a dia uma produtividade similar a Rails e Grails, no entanto, utilizando Java, Spring, Hibernate e Wicket. Num cenário como este manter o repositório se tornou uma guerra contra esses frameworks, mesmo. Isso se agravou principalmente por causa do Wicket, já que não é tão complicado criar componentes de UI um pouco mais espertos.[/quote]
Só reparei que talvez sua dificuldade para usar os respositorios esteja exatamente na aplicaçào do smart-UI ou componentes UI mais espertos. Não digo que é certo ou errado, mas muitos, inclusive o Eric Evans menciona no livro, o pattern Smart-UI exclui mutuamente o uso do MDD, permitindo o uso das outras pŕaticas do DDD.[/quote]

Humm, Smart-UI? Não li este capítulo! Valeu pela dica, Filipe. :wink:

Veja o link abaixo. É muito interessante e trata sobre o Smart-UI.
http://codeidol.com/csharp/domain-driven-design/Isolating-the-Domain/The-Smart-UI-Anti-Pattern/

Em português tem esse aqui ó:
http://andersonleiteblog.wordpress.com/2009/03/07/model-driven-design-e-isolamento-do-dominio/

Veja o link abaixo. É muito interessante e trata sobre o Smart-UI.
http://codeidol.com/csharp/domain-driven-design/Isolating-the-Domain/The-Smart-UI-Anti-Pattern/

Em português tem esse aqui ó:
http://andersonleiteblog.wordpress.com/2009/03/07/model-driven-design-e-isolamento-do-dominio/[/quote]

Valeu, acabei de dar uma lida em ambos para comentar aqui. :wink: (depois leio com mais calma)

Curti os artigos. A única parte assustadora é que de cara eles citam o Smart-UI como um anti-pattern. Com certeza, se considerar que o Smart-UI faz selects na base, sem dúvida nenhuma a lista de desvantagens será grande.

Já no caso como estou implementando tá rolando algo um pouco diferente, talvez, um Semi-Smart-UI (rsrs). A lógica de negócio e queries ainda continuarão (ou pelo meno devem continuar) no domínio.

Quero também destacar este trecho:

Eu tenho tomado este cuidado. Bom saber que mesmo ousando na camada de UI é possível se falar em DDD.

[quote=Thiago Senna]
Valeu, acabei de dar uma lida em ambos para comentar aqui. :wink: (depois leio com mais calma)

Curti os artigos. A única parte assustadora é que de cara eles citam o Smart-UI como um anti-pattern. Com certeza, se considerar que o Smart-UI faz selects na base, sem dúvida nenhuma a lista de desvantagens será grande.

Já no caso como estou implementando tá rolando algo um pouco diferente, talvez, um Semi-Smart-UI (rsrs). A lógica de negócio e queries ainda continuarão (ou pelo meno devem continuar) no domínio.

Quero também destacar este trecho:

Eu tenho tomado este cuidado. Bom saber que mesmo ousando na camada de UI é possível se falar em DDD.[/quote]

Na verdade o Smart-UI é um anti-pattern no contexto do Model Driven Development. Se você não se propor a usar o MDD, então o Smart-UI não é um Anti-Pattern.

O DDD é um processo de práticas, onde uma complementa a outra. Se usarmos várias práticas do DDD mas não usarmos o MDD, estamos também fazendo DDD?
Eu acho que sim.

Não gosto de pensar que quem faz DDD é o bom e quem não faz é o ruim. Gosto de pensar que o DDD nos sugere uma série de práticas por um motivo. Essas práticas se justificam pelos benefícios que elas apresentam. Então eu gosto de pensar: Tal projeto está usufruindo dos benefícios totais. Tal projeto está usufruindo de parte dos benefícios, pois não aplica todas as práticas. Tais projetos não se beneficiam por que não aplicam boas práticas.

Assim, acho mais justo. No seu caso, você deve estar se beneficiando pelo menos de algumas práticas. Talvez até possa evoluir para ter ainda mais benefícios. Talvez nem precise. Cabe a você julgar isso. =)

Entendo, entendo. Mas em minha opinião este esforço não tem valido a pena.
[/quote]

Bom, então só posso concluir que vc não faz testes automáiticos no seu software… porque só para facilitar os testes já é uma benção para mim.

Fazer bom OO é mais facil que fazer mau OO. Eu sei que é dificil de acreditar, mas é verdade. Vc codifica menos e com mais gosto.
O MiddleHEaven é por causa do principio DRY , porque ficar implementando a mesma coisa 300 vezes ? e já que vai fazer , faz direito ( easy said than done)

Hum… provavelmente porque usa os framework errados.Escolher frameworks com flexibilidade suficiente não é simples.

[quote]
Hoje, utilizando wicket, ainda posso colocar o interpreter fora do repositorio. Eu poderia numa boa criar um componente de UI que fizesse a interpretação do QueryObject. Melhor fazer isso do que criar o componente de UI, que chama o repositório e que por fim, chama o QueryObject. IMHO é muita delegação. A maioria dos projetos não precisam ser defensivos a este ponto, acho. E vocês, o que acham?[/quote]

Para mim QO é um objeto de contexto , ele viaja entre camadas , mas não desde da UI. Essa de fazer um interpretador na UI não entendi. O interpretador deveria estar perto do repositorio / hibernate / entitymanger e não longe na UI.
Em vb usava smart-UI e sempre achei acoplado demais. Por isso que gosto de java e OO porque vc pode (deve?) não usar smart-UI.

smart-UI para mim é usar Jquery fazendo tabs e menus dinamicos e até ajax… mas as logicas de manipulação, qeury, etc é tudo no codigo back-end por causa da reusabilidade.

Pessoal, que bom que o post tenha dado esta discursão tão produtiva para a comunidade.

realmente é bom ter várias cabeças pensando junto, estou implementando uma abordagem de Fluent interfaces para o meu Repositorio onde tenho métodos que fazem chamada da seguinte forma:

usuarioRepositorio.igual("nome","Carlos Alberto").igual("ativo",true).ordenadoPor("nome",descending).listagem(1,40);
usuarioRepositorio.igual("nome","Carlos Alberto").igual("ativo",true).ordenadoPor("nome",descending).contagem();

a linha 1 me retorna uma lista de todos os usuários com o nome carlos alberto que estejam ativos e ordenado por nome na primeira pagina e retorna 40 registros.
a linha 2 faz a mesma coisa mas me retorna a contagem.

caso tenha um critério mais especifico implemento um método dentro do repositorio especificamente para atender a demanda do mesmo.

assim o cliente do repositorio pode selecionar de forma livre os dados que deseja e ainda implementar a paginação e ordenamento.

Crio os testes automatizados numa boa. Conclusão precipitada, não acha?

Claro, sem dúvida.

Muito boa sua iniciativa. O código parece bem legal também. No entanto, tenho preferido evitar todo este esforço. É uma guerra que nunca termina para seu projeto não ficar acoplado a framework nenhum. Enquanto não houver algo pronto que eu possa reaproveitar prefiro resolver as coisas de formas simples e facilmente refatoráveis. Acho que ambas idéias são aceitáveis.

Será mesmo? Acho que fazer o contrário é tão difícil quanto, ou seja, adaptar o domínio aos benefícios dos frameworks.

Se eu optar por não usar repositórios, vou fazer como? No meu contexto usar o QueryObject como se fosse um Bean junto com as classes de UI tem trazido ótimos benefícios e reduzido muito código.

Pois é, enfiei o hibernate dentro das classes de UI.

[quote=sergiotaborda]Em vb usava smart-UI e sempre achei acoplado demais. Por isso que gosto de java e OO porque vc pode (deve?) não usar smart-UI.

smart-UI para mim é usar Jquery fazendo tabs e menus dinamicos e até ajax… mas as logicas de manipulação, qeury, etc é tudo no codigo back-end por causa da reusabilidade.[/quote]
O que estou implementando não é tão radical quanto se propõe o Smart-UI. Só que o ideal é que o Domain possa ser facilmente acessado e manipulado por este “Smart-UI”. Por isso o repositorio pra mim não tem ajudado. Mantê-lo implica em escrever bem mais código. Simples assim.

[quote=euprogramador]Pessoal, que bom que o post tenha dado esta discursão tão produtiva para a comunidade.

realmente é bom ter várias cabeças pensando junto, estou implementando uma abordagem de Fluent interfaces para o meu Repositorio onde tenho métodos que fazem chamada da seguinte forma:

usuarioRepositorio.igual("nome","Carlos Alberto").igual("ativo",true).ordenadoPor("nome",descending).listagem(1,40);
usuarioRepositorio.igual("nome","Carlos Alberto").igual("ativo",true).ordenadoPor("nome",descending).contagem();

[/quote]
Se se trata de fluent interface, eu entendo que você está listando repositórios ao invés de usuários. Como eu li:

“listagem de usuarioRepositorio igual Carlos Alberto e igual ativo, ordeando por nome.”

Talvez se fosse usuario.igual("nome","Carlos Alberto").igual("ativo",true).ordenadoPor("nome",descending).listagem(1,40);
Aí sim, ficaria bem melhor.
Ainda daria pra otimizar, já que faz parte do usuário mesmo, pode diminuir o uso de parametros e passar a usar metodos com o nome real e de quebra, ainda eliminaria a dependencia que o repositorio tem da estrutura do seu aggregate. Imagine se vc remover o campo ativo. Causará side effect em várias chamadas desse repositorio.

Outra coisa que reparei é que nesse caso aí você está criando um query object builder, como o taborta mencinou no outro tópico. Esse código é muito bonito pra infra estrutura, mas no repositório eu colocaria algo mais objetivo. Para ter uma idéia melhor do que estou falando, procure ler sobre o Supple Design. Ele determina práticas como IntentionReavelingInterfaces de forma que sua interface revela o que deve ser feito.

Pode chamar esse query object builder direto de dentro do repositorio se quiser, mas leve em consideração o que eu escrevi acima. =)

O que acontece é o seguinte eu tenho os ManagedBeans do jsf que pertence a camada de aplicação, e coloquei a interface do repositório na camada de dominio e a implementação na infraestrutura, desta forma que demonstrei o repositório realmente sabe demais sobre a infraestrutura, o que recomenda então é criar um objeto que será responsável por acessar o banco e ficará na infraestrutura, e tanto o managed bean e o repositório o acessaria?

ficando assim

...
// método do managed bean
public void recuperarListagem(){
    listaDeClientes = usuario.igual("nome","Carlos Alberto").igual("ativo",true).ordenadoPor("nome",descending).listagem(1,40);  
}
...

O repositório seria também poderia acessar o QueryObject, mas agora ele tem uma interface mais especifica como:

...
// método da interface do repositorio
public List<Usuario> recuperarUsuariosNaoAtivos();
...

Com a seguinte implementação:

...
// método da classe que implementa o repositorio
public List<Usuario> recuperarUsuariosNaoAtivos(){
    return usuario.igual("ativo",false).ordenadoPor("nome",descending).listagem();  
}

Seria assim?

Outra, o que tenho notado com DDD é que o código de negócio (Service) fica bem pequeno, mas temos muito código nas camadas para suportar o serviço, é assim mesmo?