.NET - Apresentando o padrão de projeto Repository


O padrão de projeto Repository acrescenta uma camada de abstração no topo da camada de consultas e ajuda eliminar lógica duplicada na implementação do código de suas consultas ao modelo de entidades.

Foi Martin Fowler que definiu o padrão Repository no seu livro - Patterns of Enterprise Application Architecture - da seguinte forma: "Intermedeia entre o domínio e as camadas de mapeamento de dados usando uma interface de coleção para acessar objetos de domínio." (numa tradução livre by Macoratti)

Neste artigo apresento os princípios do padrão de projeto Repository e como podemos implementá-lo usando LINQ e Entity Framework 4.1.

Para poder reproduzir o código usado neste artigo é recomendável que você utilize o Visual Studio 2010 ou o Visual Studio 2008.

O que é o padrão Repository ?

Um repositório é essencialmente uma coleção de objetos de domínio em memória, e, com base nisso o padrão Repository permite realizar o isolamento entre a camada de acesso a dados (DAL) de sua aplicação e sua camada de apresentação (UI) e camada de negócios (BLL).

Ao utilizar o padrão Repository você pode realizar a persistência e a separação de interesses em seu código de acesso a dados visto que ele encapsula a lógica necessária para persistir os objetos do seu domínio na sua fonte de armazenamento de dados.

Em suma, você pode usar o padrão Repository para desacoplar o modelo de domínio do código de acesso a dados.

Martin Fowler afirma: "O padrão  Repository faz a mediação entre o domínio e as camadas de mapeamento de dados, agindo como uma coleção de objetos de domínio em memória.....Conceitualmente, um repositório encapsula o conjunto de objetos persistidos em um armazenamento de dados e as operações realizadas sobre eles, fornecendo uma visão mais orientada a objetos da camada de persistência.....e também da suporte ao objetivo de alcançar uma separação limpa e uma forma de dependência entre o domínio e as camadas de mapeamento de dados." (http://martinfowler.com/eaaCatalog/repository.html)

Em uma das implementações do padrão repositório podemos começar definindo uma interface que atuará como a nossa fachada de acesso aos dados conforme mostrado abaixo:

public interface IClienteRepository
{
   Cliente GetByID(int clienteID);
  
Cliente Load(int clienteID);
  
void Save(Cliente cliente);
  
void Delete(Cliente cliente);
}
Public Interface IClienteRepository

   Function GetByID(_clienteID As Integer) As Cliente
  
Function Load(_clienteID As Integer) As Cliente
  
Sub Save(_cliente As Cliente)
  
Sub Delete(_cliente As Cliente)

End Interface

C# VB .NET

Definimos a interface para o repositório Cliente mas podemos ter outras interfaces para outros repositórios como Produto, Pedido, Fornecedor, etc. Abaixo temos a interface para o repositório Produto:

public interface IProdutoRepository
{
   Produto GetByID(int produtoID);
  
Produto Load(int produtoID);
  
void Save(Produto produto);
  
void Delete(Produto produto);
}
Public Interface IProdutoRepository

   Function GetByID(_produtoID As Integer) As Produto
  
Function Load(_produtoID As Integer) As Produto
  
Sub Save(_produto As Produto)
  
Sub Delete(_produto As Produto)

End Interface

C# VB .NET

Podemos usar o recurso Generics para generalizar estas interfaces. Abaixo temos uma implementação da interface para qualquer repositório deste tipo:

O uso de Generics basicamente consiste em tipar uma coleção de modo a garantir que somente um tipo de dado pertença a ela.

Todas as coleções genéricas do .Net framework estão agrupadas dentro do namespace System.Collections.Generics As principais são:

LinkendList<> - Organiza os itens na forma de uma lista duplamente ligada.
List<> - Organiza os itens na forma de uma lista tradicional (como um ArrayList).
Queue<> - Organiza os itens na forma de uma fila.
SortedList<> - Organiza os itens na forma de uma lista indexada.
SortedDictionary - Organiza os itens na forma de um dicionário.
Stack<> - Organiza os teins na forma de um pilha.

Referência:
http://msdn.microsoft.com/pt-br/library/cc580580.aspx

Exemplo para repositório genérico:


public interface IRepository<T>
{
   T GetById(
int id);
   T Load(
int id);
  
void Save(T entity);
 
void Delete(T entity);
}

Public Interface IRepository(Of T)
   Function GetById(id As Integer) As T
  
Function Load(id As Integer) As T
  
Sub Save(entity As T)
  
Sub Delete(entity As T)
End Interface
C# VB .NET

Um Repositório para o LINQ

Para implementar o padrão Repository para o LINQ você primeiro precisa definir a interface que deve conter a declaração de todos os métodos que você deseja em seu repositório. O código abaixo mostra a interface que podemos usar para o padrão Repository com LINQ:


public
interface IRepository<T> where T : class
{
   IQueryable
<T> GetAll();
   void
InsertOnSubmit(T entity);
   void
DeleteOnSubmit(T entity);
   void
SubmitChanges();
}
 

Public
Interface IRepository(Of T As Class)

Function
GetAll() As IQueryable(Of T)
Sub
InsertOnSubmit(entity As T)
Sub
DeleteOnSubmit(entity As T)
Sub
SubmitChanges()

End Interface
 

C# VB .NET

Após definir a interface para o seu repositório você pode usá-lo para encapsular a forma como a recuperação, consulta e armazenamento seria realizada em seu código de acesso a dados usando o contexto de dados (data context) do LINQ.

Para isso basta implementar a interface IRepository em uma classe concreta como mostra o código abaixo:

public class Repository<T> : IRepository<T> where T : class

{

  public DataContext Context

  {

    get;

    set;

  }

  public virtual IQueryable<T> GetAll()

  {

    return Context.GetTable<T>();

  }

  public virtual void InsertOnSubmit(T entity)

  {

    GetTable().InsertOnSubmit(entity);

  }

  public virtual void DeleteOnSubmit(T entity)

  {

    GetTable().DeleteOnSubmit(entity);

  }

  public virtual void SubmitChanges()

  {

    Context.SubmitChanges();

  }

  public virtual ITable GetTable()

  {

    return Context.GetTable<T>();

  }

}
 

C#

 

Public Class Repository(Of T As Class)

    Implements IRepository(Of T)
 

Public Property Context() As DataContext

  Get

    Return m_Context

  End Get

  Set(value As DataContext)

     m_Context = value

   End Set

  End Property
 

  Private m_Context As DataContext
 

  Public Overridable Function GetTable() As ITable

     Return Context.GetTable(Of T)()

  End Function


  Public
Sub DeleteOnSubmit(entity As T) Implements IRepository(Of T
).DeleteOnSubmit

    GetTable().DeleteOnSubmit(entity)

  End Sub

  Public Function GetAll() As System.Linq.IQueryable(Of T) Implements IRepository(Of T).GetAll

     Return Context.GetTable(Of T)()

  End Function

  Public Sub InsertOnSubmit(entity As T) Implements IRepository(Of T).InsertOnSubmit

     GetTable().InsertOnSubmit(entity)

  End Sub

  Public Sub SubmitChanges() Implements IRepository(Of T).SubmitChanges

    Context.SubmitChanges()

  End Sub

End Class
 

VB .NET

Um repositório para o Entity Framework

A implementação do padrão Repository usando o ADO .NET Entity Framework segue as mesmas regras definidas anteriormente: primeiro definimos a interface e a seguir a sua implementação.

A seguir temos um exemplo de definição da interface genérica para o Ef 4.1 :

public interface IRepository<T>

{

   T GetById(int id);

   T[] GetAll();

   IQueryable<T> Query(Expression<Func<T, bool>> filter);

   void Save(T entity);

   void Delete(T entity);

}


 

Public Interface IRepository(Of T)

Function GetById(id As Integer) As T

Function GetAll() As T()

Function Query(filter As Expression(Of Func(Of T, Boolean))) As IQueryable(Of T)

Sub Save(entity As T)

Sub Delete(entity As T)

End Interface


 

C#

VB .NET

Após a definição da interface podemos usá-la para implementar nossas classes do repositório. No código a seguir temos um exemplo de implementação para a classe ClienteRepository:

public class ClienteRepository : IRepository<Cliente>, IDisposable

{

private _entidades _context;
 

 public ClienteRepository()

 {

  _context = new _entidades();

 }

 
 public
Cliente GetById(int id)

 {

   return _context.Clientes.

   Where(s => s.ClienteID == id).

   FirstOrDefault();

 }


 
public Cliente[] GetAll()

 {

   return _context.Clientes.ToArray();

 }


 
public IQueryable<Cliente> Query(Expression<Func<Cliente, bool>> filter)

 {

   return _context.Clientes.Where(filter);

 }


 
public void Save(Cliente cliente)

 {

   //código para persistir o registro do cliente para o banco de dados

 }


 
public void Delete(Cliente cliente)

 {

   //Código necessário para deletar um cliente

 }


 
public void Dispose()

 {

   if (_context != null)

   {

     _context.Dispose();

   }

   GC.SuppressFinalize(this);

 }

}

 


 

Public Class ClienteRepository

   Implements IRepository(Of Cliente)

   Implements IDisposable


Private
_context As _entidades


Public
Sub New()

_context = New _entidades()

End Sub


Public
Sub Delete(entity As Cliente) Implements IRepository(Of Cliente).Delete

   'Código necessário para deletar um cliente

End Sub


Public
Function GetAll() As Cliente() Implements IRepository(Of Cliente).GetAll

  Return _context.Clientes.ToArray()

End Function


Public
Function GetById(id As Integer) As Cliente Implements IRepository(Of Cliente).GetById

   Return _context.Clientes.Where(Function(s) s.ClienteID = id).FirstOrDefault()

End Function


Public
Function Query(filter As System.Linq.Expressions.Expression(Of System.Func(Of Cliente, Boolean))) As System.Linq.IQueryable(Of Cliente) Implements IRepository(Of Cliente).Query

  Return _context.Clientes.Where(filter)

End Function
 

Public Sub Save(entity As Cliente) Implements IRepository(Of Cliente).Save

'código para persistir o registro do cliente para o banco de dados

End Sub


Public
Sub Dispose() Implements System.IDisposable.Dispose

  If _context IsNot Nothing Then

   _context.Dispose()

  End If

  GC.SuppressFinalize(Me)

End Sub
 

End Class


 

C#

VB .NET

Lições aprendidas

O padrão Repository fornece assim uma maneira simples para encapsular o código de acesso de dados em seu aplicativo permitindo também que o código seja mais facilmente testável e que os módulos de código sejam mais facilmente reutilizáveis; além disso ele favorece a separação de interesses entre a lógica de acesso a dados e lógica de domínio do aplicativo.

Em essência, o padrão Repository promove a testabilidade e o uso de injeção de dependência, reduz o acoplamento ou a coesão entre os componentes de acesso a dados e o modelo de domínio do aplicativo e abstrai a maneira como o código de acesso de dados é escrito em suas aplicações.

O código mostrado no artigo é apenas uma das maneiras de implementarmos o padrão Repository no LINQ e no Entity Framework 4.1. (Eu disse uma das maneiras e não a única maneira)

Obs: No artigo Entity Framework 4.1 - Conceitos - Usando o padrão Repository (inativo) vermos um exemplo completo para o Entity Framework.

"Eu sou o Alfa e o Ômega, o princípio e o fim, o primeiro e o derradeiro.  Bem-aventurados aqueles que lavam as suas vestiduras no sangue do Cordeiro, para que tenha direito à árvore da vida, e possam entrar na cidade pelas portas." Apocalipse 22:13-14



Referências:


José Carlos Macoratti