Domain-Driven Desing não é sobre padrões - isso gera muita discussão. Domain-Driven Design (ou simplesmente DDD) é sobre fazer software de maneira mais simples - e sei que muitos não vão concordar com isso, pois “parece” que é mais fácil e muitos se sentem muitos produtivos ligando Table Modules diretamente às telas e abusando de Transaction Scripts.
O objetivo deste artigo não é falar sobre o que a literatura diz sobre repositórios e nem tenho a pretensão de escrever o “post definitivo sobre Repositórios”. O objetivo aqui é demonstrar como tenho aplicado Repositórios nesses 3 anos de experiência em aplicação e ensino de DDD.
O que são Repositórios afinal?
Antes de mais nada, orientamos a aplicação ao domínio. O exemplo que vou montar aqui não é real, mas é bem próximo daquilo que apliquei em alguns projetos. Orientar a aplicação ao domínio significa que seu código será limpo e principalmente sua camada de negócios demonstrará o NEGÓCIO, suas regras, seu estado e suas associações. Se sua aplicação é de vendas, é esperado que o vocabulário do domínio “vendas” seja encontrado nas suas classes. As classes do domínio também não devem ser poluídas com o domínio técnico: o domínio “vendas” não sabe o que são tabelas, filas, sistema de arquivos e nem SMTP. O domínio técnico e do domínio do negócio tentam ao máximo não se misturar numa aplicação Domain-Driven.
Se minhas classes de negócio traduzem a linguagem de vendas, quero que você tente pensar num modelo que resolva os seguintes requisitos:
1. Um pedido de vendas possui cliente e itens
2. O valor total do pedido é a soma do valor dos itens
3. Somente produtos disponíveis podem ser vendidos
Imagine agora, como um ator Vendedor utilizaria o sistema. Ele criaria um pedido, colocaria alguns itens com produtos disponíveis e finalizaria este caso de uso. Quero que você pense bastante em como você resolveria o requisito número 3. No meu código, surgiria algo deste tipo:

A primeira pergunta natural é: Por que o repositório está como uma interface? Muitas vezes o Repositório simplesmente é uma abstração do domínio cuja responsabilidade é representar coleções de entidades, porém, ele ainda é um elemento do domínio. Muitas vezes nós temos um elemento que realiza esta interface para fornecer implementação para essas buscas.

Usar repositórios como interfaces é bom para a saúde da testabilidade da sua aplicação e ainda posso me beneficiar de injeção de dependência. Isso facilita o uso de Mocks e Stubs para testes unitários ou de integração, além de deixar seus objetos desacoplados.
Teve algumas situações peculiares onde a interface TodosProdutos era um elemento do domínio e a implementação TodosProdutosImpl era puramente infra-estrutura. Neste caso, TodosProdutos (a interface) estava empacotada no domínio (geralmente uso o nome domain) e a TodosProdutosImpl estava empacotado como infra-estrutura.
De qualquer forma, para a discussão aqui, simplesmente destaco que o repositório TodosProdutos é algo puramente do vocabulário de negócios. “Todos os Produtos” é algo que o meu usuário sabe o que significa. E TodosProdutos.getDisponiveis - a minha operação - faz mais sentido ainda para os usuários, pois eles também sabem o que são produtos disponíveis para venda (se isso para eles é uma interface ou uma classe isso é completamente irrelevante).
Repositórios como Coleções Parciais
Teve uma certa aplicação que desenvolvi que nas conversas com os especialistas de negócio um determinado termo era muito recorrente. Eles se referiam a um determinado conjunto de entidades com um nome especial: pedidos faturados. E nesse sistema, os “pedidos faturados” tinham uma força tão grande na Ubiquitous Language da DDD que passou a ser um contexto separado.

Várias vezes mostrei esse diagrama a arquitetos e programadores e muitos não entenderam isso facilmente. É bastante comum as pessoas associarem um repositório por entidade, enxergando-os como 1 para 1 com DAOs. Muitos desses programadores que não entendem DDD questionaram: - Por que separar isso? Minha resposta: - Não fui eu que separei isto… no domínio do negócio isso é separado. Não é uma opção arquitetural, somente estou transparecendo o domínio.
A maioria das respostas sobre a camada de negócios numa aplicação Domain-Driven é dada pelos usuários e especialistas do domínio e não pelos arquitetos e programadores.
O nome não deve ser XptoRepository?
Durante algum tempo, seguindo a linha do Hibernate, minhas classes de busca eram [NomeDaEntidade]DAO. Depois de um tempo meus DAOs eram repositórios, mas continuava chamando-os de DAOs, pois odeio o nome Repository dentro do meu código. Acho pior ainda o nome “Repositório” - que é uma tradução livre corrente. O nome Repositório é bem pouco expressivo em português (que raios é repositório?) - o termo em inglês é mais claro.
Ao fim de tudo isso, vejo que é bem pouco proveitoso chamar uma classe de domínio de PedidoRepository ou PedidoRepositorio. Teve uma aplicação que chamei meus repositorio de “Pedidos”, mas últimamente tenho usado mesmo “TodosPedidos”, “TodosClientes”, “PedidosFaturados”, “ClientesInadimplentes” e etc… Uso esses termos pois são mais próximos do domínio e não vejo a mínima razão para ter o nome do padrão no nome ou empacotamento das classes.
Pedido é uma entidade, nem por isso o chamamos de PedidoEntity, o mesmo vale para Value Objects, Services e Repositórios. O BufferedInputStream do Java é um Decorator, nem por isso chamamos ele de BufferedInpuStreamDecorator.
Repositórios mais Complexos
Associar repositórios com buscas nem sempre é verdadeiro. Repositórios respondem por conjuntos de entidades, não importando a dificuldade de se obter esse conjunto. Há repositórios que podem ter código de negócio complexo em sua execução onde determinadas operações podem à primeira vista aparentar simplesmente uma busca. Você deve levar em consideração se há inteligência de domínio envolvida, caso afirmativo seu repositório deverá ter essa inteligência embutida. Vamos colocar mais requisitos:
4. Se o pedido é para entrega imediata, os produtos disponíveis são aqueles que eu tenho em estoque no momento da venda
5. Se o pedido é para entrega futura, os produtos disponíveis são aqueles que estarão em estoque na data de entrega

Neste “TodosProdutos” refatorado, para obter os produtos disponíveis na data da entrega é necessário pegar o saldo atual do estoque, subtrair os pedidos de venda já comprometidos (saídas) e somar os pedidos de compra (entradas) até a data desejada. Tudo isso é inteligência de domínio que não deve ser delegado para o banco de dados. É um repositório que tem regras e pode até orquestrar junto a outros repositórios e services para cumprir o seu propósito.
Um ponto importante a destacar é que muitas outras variações de implementações de repositórios existem. O objetivo do artigo é somente destacar algumas implementações interessantes que implementei em alguns projetos.
É lícito acessar o EntityManager diretamente?
Discussões sobre Domain-Driven e implementações de respositórios são muito recorrentes em listas de discussões, principalmente no GUJ e na DotNetArchitects (sou novo nessa lista).
Uma das perguntas comuns é: “É licito eu acessar diretamente o EntityManager ou Sessão do Hibernate?”. Particularmente eu sou da opinião que se você está fazendo buscas que não são importantes para o negócio não há problemas acessar diretamente seu framework de persistência. Logicamente que isso não deve ser levado a ferro e a fogo, testabilidade e complexidade são pontos a considerar.
Um exemplo seria: imagine que para emitir o pedido eu tenha que preencher um combo com o tipo de venda (um dado simplesmente informativo). Se é uma busca de uma entidade fraca, que significa muito pouco para o domínio, para reduzir a complexidade, o repositório pode ser descartado sem muito prejuízo para a testabilidade.