Prisma ORM: Melhores Práticas para Desenvolvedores Seniores

Prisma ORM: Melhores Práticas para Desenvolvedores Seniores





Melhores práticas de Prisma ORM para seniors



1. Arquitetura do Prisma Client e ciclo de vida

  • Instância única do PrismaClient: crio uma única instância por aplicação e a reutilizo entre requisições para evitar overhead de reconexões e consumo de recursos. Em ambientes serverless, exponho a instância via global para manter reuso entre invocações.
  • Separação entre domínio e persistência: o Prisma schema reflete o modelo de dados persistente, enquanto as regras de negócio residem na camada de domínio (serviços/use-cases). DTOs ajudam a isolar a API dos modelos de dados internos.
  • Versionamento de migrations: utilizo Prisma Migrate em pipelines de CI/CD. Mantenho migrations sob controle de versão, evitando alterações manuais diretas no banco. Desenvolvimento com prisma migrate dev; produção com prisma migrate deploy.
  • Geração de tipos e produtividade: manter o PrismaClient gerado de forma previsível facilita tipagem estática no TypeScript e reduz erros de compile-time.

2. Consultas eficientes e padrões de modelagem

  • Consiga o mínimo necessário: prefira selecionar apenas os campos que serão usados com select, ou traga relações apenas quando necessário com include. Evito fetch de dados desnecessários para reduzir payloads e consumo de memória.
  • Combinação inteligente de relações: quando precisar de dados de entidades relacionadas, utilize include com filtragem interna e limites (take) para evitar N+1 e excesso de dados.
  • Paginação baseada em cursor: para grandes volumes, use cursor-based pagination com take e orderBy, mantendo a posição entre páginas sem contagens custosas.
  • Contagens eficientes: se houver necessidade de contagem, use _count para obter números sem retornar registros completos.
  • Indexação relevante: posicione índices com @index e @unique em colunas com alto volume de leitura/filtragem. Pense em padrões de consulta comuns da aplicação ao projetar o schema.
// Exemplo: busca de posts com dados do autor e contagem de comentários
const posts = await prisma.post.findMany({
  where: { published: true },
  take: 20,
  orderBy: { createdAt: 'desc' },
  include: {
    author: { select: { id: true, name: true } },
    comments: { where: { approved: true }, take: 5, select: { id: true } }
  },
  _count: { select: { comments: true } }
});

3. Transações e consistência de dados

  • Transações atômicas: use prisma.$transaction para envolver múltiplas operações que precisam ser atômicas. Em caso de erro, a operação é revertida automaticamente.
  • Estratégias de transação: é possível usar o padrão de callback (transaction(async tx) => { … }) ou o modo baseado em array (prisma.$transaction([ … ])) para cenários simples.
  • Isolamento: se a base de dados suportar, defina o nível de isolamento desejado (por exemplo, Serializable) para evitar condições de corrida em cenários críticos.
  • Tratamento de erros: utilize blocos try/catch para capturar falhas, mantendo logs essenciais e propagando erros de forma consistente para o chamador.
// Exemplo: transferência de saldo entre contas em uma transação
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();

async function transferirSaldo(fromUserId: number, toUserId: number, amount: number) {
  await prisma.$transaction(async (tx) => {
    const from = await tx.account.findUnique({ where: { userId: fromUserId }, select: { balance: true } });
    if (!from || from.balance < amount) throw new Error('Saldo insuficiente');

    await tx.account.update({ where: { userId: fromUserId }, data: { balance: { decrement: amount } } });
    await tx.account.update({ where: { userId: toUserId }, data: { balance: { increment: amount } } });
  }, { isolationLevel: 'Serializable' });
}

// Chamada de exemplo
// transferirSaldo(1, 2, 100).catch(err => console.error(err));

4. Modelagem de dados, migrações e observabilidade

  • Modelagem clara: use enums para campos com valores restritos, defina relações nomeadas e evite chaves compostas sem necessidade.
  • Indices e unicidade: aplique @index e @unique nos campos mais filtrados para melhorar desempenho de queries comuns.
  • Auditoria e soft delete: inclua campos como deletedAt ou criadoPor para rastrear alterações sem perder histórico; use filtros explícitos para excluir itens deletados quando necessário.
  • Seeds e reprodutibilidade: utilize prisma db seed para datasets iniciais em dev e staging, mantendo scripts versionados.
  • Observabilidade: adicione middleware simples para log de queries e métricas de tempo de resposta sem depender de ferramentas externas. Isso ajuda a identificar gargalos sem atrapalhar o fluxo de desenvolvimento.
// Exemplo de schema.prisma (trecho ilustrativo)
generator client {
  provider = "prisma-client-js"
}
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String?
  posts     Post[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String?
  published Boolean  @default(false)
  authorId  Int
  author    User     @relation(fields: [authorId], references: [id])
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([authorId, published])
}

Bloco de código relevante

Exemplo de consulta com contagem e paginação baseada em cursor, mantendo o desempenho para listas longas.


// Exemplo de paginação baseada em cursor com contagem de itens de cada item
async function buscarUsuariosPaginados(prisma: PrismaClient, lastId?: number, limit = 20) {
  const users = await prisma.user.findMany({
    take: limit,
    ...(lastId ? { cursor: { id: lastId }, skip: 1 } : {}),
    orderBy: { id: 'asc' },
    select: {
      id: true, email: true, name: true,
      _count: { select: { posts: true } } // contagem de posts por usuário
    }
  });
  return users;
}

Gostou deste guia técnico?

Confira outros posts da série Prisma ORM para seniors, com foco prático, arquitetura, performance e manutenção.

Leia mais posts