Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Repository Pattern no Node.js: isole persistência

Atualizado em: 13 de outubro de 2026

Estação de trabalho usada no desenvolvimento de aplicações Node.js

Repository Pattern no Node.js organiza o acesso a dados atrás de uma interface orientada ao domínio. Em vez de espalhar SQL, chamadas de ORM ou detalhes de cache pelos serviços, a aplicação depende de operações como buscarPorId, salvar e remover.

O padrão ajuda quando existe lógica de persistência relevante, mais de uma implementação ou necessidade de testes isolados. Ele não deve virar uma camada genérica que apenas repete todos os métodos do ORM.

Neste guia, você aprenderá contratos, implementação com banco, transações, paginação, fakes, erros e boas práticas.

Problema sem repository

export async function ativarUsuario(id, pool) {
  const result = await pool.query(
    'SELECT * FROM users WHERE id = $1',
    [id]
  );

  if (!result.rows[0]) throw new Error('Não encontrado');

  await pool.query(
    'UPDATE users SET active = true WHERE id = $1',
    [id]
  );
}

A regra de negócio conhece SQL, estrutura de tabela e formato do driver.

Contrato do repository

export function criarUsuarioService({ userRepository }) {
  return {
    async ativar(id) {
      const user = await userRepository.buscarPorId(id);
      if (!user) throw new UserNotFoundError(id);

      user.active = true;
      await userRepository.salvar(user);
      return user;
    }
  };
}

O serviço trabalha com entidades e operações do domínio.

Implementação PostgreSQL

export function criarUsuarioRepository(pool) {
  return {
    async buscarPorId(id) {
      const result = await pool.query(
        'SELECT id, email, active FROM users WHERE id = $1',
        [id]
      );
      return result.rows[0] ?? null;
    },

    async salvar(user) {
      await pool.query(
        `UPDATE users
         SET email = $2, active = $3
         WHERE id = $1`,
        [user.id, user.email, user.active]
      );
    }
  };
}

Mapeamento de dados

Não devolva linhas do banco diretamente quando nomes, tipos ou nulabilidade diferem do domínio:

function mapRow(row) {
  return {
    id: row.id,
    email: row.email,
    active: Boolean(row.active),
    createdAt: new Date(row.created_at)
  };
}

Consultas orientadas ao caso de uso

Evite um repository genérico com dezenas de filtros opcionais. Métodos específicos comunicam intenção:

  • buscarAtivosPorOrganizacao;
  • listarExpiradosAntesDe;
  • reservarParaProcessamento;
  • existeEmail.

Paginação

O contrato deve definir cursor, ordenação e limite. Não exponha detalhes do ORM como contrato público do domínio.

await repository.listar({
  after: cursor,
  limit: 50,
  status: 'active'
});

Transações

Uma operação de negócio pode envolver vários repositories. A transação deve ser coordenada pela unidade de trabalho ou pelo serviço de aplicação, sem cada repository abrir sua própria transação.

await database.transaction(async (tx) => {
  const users = criarUsuarioRepository(tx);
  const audit = criarAuditRepository(tx);

  await users.salvar(user);
  await audit.registrar(event);
});

Repository e cache

Cache pode decorar um repository:

function comCache(repository, cache) {
  return {
    async buscarPorId(id) {
      const cached = await cache.get(id);
      if (cached) return cached;
      const value = await repository.buscarPorId(id);
      if (value) await cache.set(id, value);
      return value;
    }
  };
}

Defina invalidação e consistência. Veja Cache HTTP no Node.js e Cache Stampede no Node.js.

Fake em memória

export function criarUsuarioRepositoryFake(initial = []) {
  const data = new Map(initial.map((item) => [item.id, structuredClone(item)]));

  return {
    async buscarPorId(id) {
      return structuredClone(data.get(id) ?? null);
    },
    async salvar(user) {
      data.set(user.id, structuredClone(user));
    }
  };
}

O fake deve respeitar o mesmo contrato da implementação real.

Testes de contrato do repository

Execute uma suíte compartilhada contra o fake e o banco real:

  • retorna null quando ausente;
  • preserva tipos;
  • aplica unicidade;
  • respeita ordenação;
  • trata concorrência;
  • faz rollback.

Erros de infraestrutura

Não transforme qualquer erro do banco em “não encontrado”. Preserve a causa e converta somente violações conhecidas para erros do domínio.

Repository com ORM

Mesmo usando ORM, o padrão pode proteger o domínio de detalhes de query. Porém, se a aplicação é CRUD simples e o ORM já é a interface adequada, uma camada extra pode apenas adicionar código.

Dependency Injection

Forneça o repository ao serviço pelo composition root. Consulte Dependency Injection no Node.js.

Erros comuns

  • criar repository genérico universal;
  • expor objetos do ORM;
  • duplicar toda a API do driver;
  • abrir transação em cada método;
  • esconder queries ineficientes;
  • fake com comportamento diferente;
  • capturar todos os erros como domínio;
  • usar o padrão sem necessidade.

Checklist

  • contrato orientado ao domínio;
  • mapeamento explícito;
  • queries observáveis;
  • transações coordenadas;
  • paginação definida;
  • fake testado;
  • erros preservados;
  • sem vazamento de ORM.

Conclusão

Repository Pattern no Node.js cria uma fronteira clara entre regras de negócio e persistência. Ele é valioso quando o acesso a dados possui comportamento próprio ou quando a aplicação precisa trocar e testar implementações.

Mantenha contratos pequenos e específicos. O repository deve representar necessidades do domínio, não esconder o banco atrás de uma camada genérica.

Consulte a descrição do padrão Repository por Martin Fowler e a documentação de testes do Node.js.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita