Bem, você pode ver um bom exemplo no Spring Data Framework, que é baseado no conceito de repositórios.
Lá você verá repositórios que lidam apenas com o armazenamento de dados e raramente contêm lógica comercial (isso é reservado para a camada de serviço). Então, por exemplo, dê uma olhada no design deles e verá que eles têm uma interface CRUDRepository que expõe métodos para criar, destruir e recuperar entidades (entre outras coisas). Há também um PagingAndSortingRepository que adiciona funcionalidade extra para exatamente isso, resultados de classificação e paginação, etc., etc.
Portanto, essa estrutura é talvez um bom lugar para estudar um bom design de repositório.
Até onde eu sei, muitos dos conceitos implementados pelo Spring Data Framework vêm de um ótimo livro chamado Design Orientado a Domínio: Combatendo a Complexidade no Coração do Software , o livro tem uma seção inteira dedicada ao design do Repositório.
Você pode obter uma cópia dele.
Um pequeno trecho do livro explica:
O padrão REPOSITORY é uma estrutura conceitual simples para encapsular essas soluções e trazer de volta nosso foco no modelo.
Um REPOSITÓRIO representa todos os objetos de um determinado tipo como um conjunto conceitual (geralmente emulado). Ele age como uma coleção, exceto com capacidade de consulta mais elaborada. Objetos do tipo apropriado são adicionados e removidos, e o mecanismo por trás do REPOSITORY os insere ou os exclui do banco de dados. Essa definição reúne um conjunto coeso de responsabilidades por fornecer acesso às raízes dos AGREGADOS desde o início do ciclo de vida até o final.
Os clientes solicitam objetos do REPOSITORY usando métodos de consulta que selecionam objetos com base nos critérios especificados pelo cliente, geralmente o valor de determinados atributos. O REPOSITORY recupera o objeto solicitado, encapsulando o mecanismo de consultas ao banco de dados e o mapeamento de metadados. Os repositórios podem implementar uma variedade de consultas que selecionam objetos com base em qualquer critério que o cliente exija. Eles também podem retornar informações resumidas, como uma contagem de quantas instâncias atendem a alguns critérios. Eles podem até retornar cálculos de resumo, como o total em todos os objetos correspondentes de algum atributo numérico.
Um REPOSITÓRIO elimina uma carga enorme do cliente, que agora pode conversar com uma interface simples e reveladora de intenção e pedir o que ele precisa em termos de modelo. Para suportar tudo isso, é necessária muita infraestrutura técnica complexa, mas a interface é simples e conceitualmente conectada ao modelo de domínio.
Portanto:
Para cada tipo de objeto que precisa de acesso global, crie um objeto que possa fornecer a ilusão de uma coleção na memória de todos os objetos desse tipo. Configure o acesso através de uma interface global conhecida.
Forneça métodos para adicionar e remover objetos, que encapsularão a inserção ou remoção real de dados no armazenamento de dados. Forneça métodos que selecionem objetos com base em alguns critérios e retornem objetos totalmente instanciados ou coleções de objetos cujos valores de atributo atendam aos critérios, encapsulando assim a tecnologia real de armazenamento e consulta. Forneça REPOSITÓRIOS apenas para raízes AGREGADAS que realmente precisem de acesso direto. Mantenha o cliente focado no modelo, delegando todo o armazenamento de objetos e acesso aos REPOSITÓRIOS.