Pular para o conteúdo
.NETRepository pattern e Service Layer
Módulo 03Construindo a Web API

Repository pattern e Service Layer

intermediario 35 min de leitura·Atualizado · .NET 9
Resumo

Isolamos o acesso a dados atrás de repositórios e criamos uma Service Layer que a apresentação consome. Um Repository Manager e um Service Manager agrupam os repositórios/serviços com inicialização preguiçosa (Lazy).

1. Objetivos de Aprendizagem#

  • Implementar um RepositoryBase genérico e repositórios específicos.
  • Agrupar repositórios/serviços em managers.
  • Registrar tudo no contêiner de DI com o tempo de vida correto.

2. Pré-requisitos#

  • RepositoryContext e entidades (tópico 17.2).

3. Conceito#

O Repository Pattern abstrai o acesso a dados: o restante da aplicação fala com interfaces (IProdutoRepository), não com o DbContext. A Service Layer contém a lógica de aplicação e mapeia entidades para DTOs (Módulo 18).

O porquê do manager com Lazy<T>: um controller pode injetar um único IRepositoryManager e acessar manager.Produto ou manager.Categoria sob demanda, instanciando o repositório só quando usado.

4. Mão na Massa#

4.1. Setup#

Sem novos pacotes.

4.2. Implementação Passo a Passo#

  1. Base genérica em Catalog.Repository:
C#
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;

public abstract class RepositoryBase<T> where T : class
{
    protected readonly RepositoryContext Context;
    protected RepositoryBase(RepositoryContext context) => Context = context;

    public IQueryable<T> FindAll(bool trackChanges) =>
        trackChanges ? Context.Set<T>() : Context.Set<T>().AsNoTracking();

    public IQueryable<T> FindByCondition(Expression<Func<T, bool>> expr, bool trackChanges) =>
        trackChanges ? Context.Set<T>().Where(expr) : Context.Set<T>().Where(expr).AsNoTracking();

    public void Create(T entity) => Context.Set<T>().Add(entity);
    public void Delete(T entity) => Context.Set<T>().Remove(entity);
}
  1. Interface e repositório de produto:
C#
public interface IProdutoRepository
{
    Task<IEnumerable<Produto>> GetProdutosAsync(Guid categoriaId, bool trackChanges);
}

public class ProdutoRepository : RepositoryBase<Produto>, IProdutoRepository
{
    public ProdutoRepository(RepositoryContext context) : base(context) { }

    public async Task<IEnumerable<Produto>> GetProdutosAsync(Guid categoriaId, bool trackChanges) =>
        await FindByCondition(p => p.CategoriaId == categoriaId, trackChanges)
              .OrderBy(p => p.Nome)
              .ToListAsync();
}
  1. Manager com Lazy:
C#
public interface IRepositoryManager
{
    IProdutoRepository Produto { get; }
    Task SaveAsync();
}

public class RepositoryManager : IRepositoryManager
{
    private readonly RepositoryContext _context;
    private readonly Lazy<IProdutoRepository> _produto;

    public RepositoryManager(RepositoryContext context)
    {
        _context = context;
        _produto = new Lazy<IProdutoRepository>(() => new ProdutoRepository(context));
    }

    public IProdutoRepository Produto => _produto.Value;
    public async Task SaveAsync() => await _context.SaveChangesAsync();
}
  1. Registro:
C#
public static void ConfigureRepositoryManager(this IServiceCollection s) =>
    s.AddScoped<IRepositoryManager, RepositoryManager>();

4.3. Executando#

Shell
dotnet build

5. Exemplo Completo#

Program.cs (trecho):

C#
builder.Services.ConfigureSqlContext(builder.Configuration);
builder.Services.ConfigureRepositoryManager();

6. Boas Práticas e Armadilhas#

FaçaEvite
Usar AsNoTracking em leituras (via trackChanges: false)Rastrear tudo, degradando a performance de consultas somente-leitura
Registrar repositórios/serviços como ScopedSingleton em algo que depende do DbContext
Chamar SaveAsync uma vez por unidade de trabalhoSalvar dentro de cada método de repositório
Atenção

o Repository Pattern sobre EF Core é discutível — o DbContext já é um Unit of Work + Repository. Aplique-o quando ele agrega valor real (testabilidade, isolamento), não por dogma.

7. Segurança e Produção#

  • Mantenha trackChanges: false como padrão em leituras públicas para reduzir uso de memória e risco de updates acidentais.
  • Regras de negócio e autorização ficam na Service Layer, não no repositório.

8. Exercícios#

  • Fácil: crie ICategoriaRepository e adicione-o ao manager.
  • Médio: implemente uma Service Layer (IProdutoService) que use o IRepositoryManager.
  • Desafio: escreva um teste unitário do serviço usando um IRepositoryManager mockado.

9. Resumo#

Isolamos o acesso a dados com RepositoryBase genérico e repositórios específicos, agrupados por um manager Lazy. A Service Layer intermedeia a apresentação, mantendo o fluxo Onion intacto.

10. Próximos Passos#

Módulo 18: tratamos requisições GET com controllers, roteamento, DTOs e AutoMapper.

11. Referências#

17-03 — Repository pattern e Service Layer | Curso ASP.NET Core