image de chargement
Retour au glossaire

Repository Pattern

Pattern architectural qui encapsule la logique d'accès aux données, créant une abstraction entre la couche métier et la persistance.

Mis à jour le 10 janvier 2026

Le Repository Pattern est un design pattern architectural qui agit comme une couche d'abstraction entre la logique métier et la couche d'accès aux données. Il centralise les opérations de persistance (CRUD) en fournissant une interface cohérente, indépendante de la technologie de stockage sous-jacente. Ce pattern favorise la séparation des préoccupations et facilite grandement les tests unitaires en permettant l'utilisation de repositories mock.

Fondements du Repository Pattern

  • Abstraction complète de la source de données (base relationnelle, NoSQL, API, cache)
  • Interface métier orientée domaine plutôt que technique (findActiveUsers vs SELECT)
  • Centralisation de la logique de requêtage et de mapping objet-relationnel
  • Isolation de la logique métier des détails d'implémentation de la persistance

Avantages stratégiques

  • Testabilité accrue : remplacement facile par des mocks pour les tests unitaires
  • Flexibilité technologique : changement de base de données sans impacter la logique métier
  • Maintenabilité : centralisation des requêtes évitant la duplication de code
  • Séparation claire des responsabilités selon les principes SOLID
  • Optimisation centralisée (cache, pagination, filtrage) applicable à tout le système

Exemple concret d'implémentation

user.repository.ts
// Interface du repository (contrat métier)
interface UserRepository {
  findById(id: string): Promise<User | null>;
  findByEmail(email: string): Promise<User | null>;
  findActiveUsers(limit: number): Promise<User[]>;
  save(user: User): Promise<User>;
  delete(id: string): Promise<void>;
}

// Implémentation concrète avec Prisma
class PrismaUserRepository implements UserRepository {
  constructor(private prisma: PrismaClient) {}

  async findById(id: string): Promise<User | null> {
    const dbUser = await this.prisma.user.findUnique({
      where: { id },
      include: { profile: true }
    });
    return dbUser ? this.mapToDomain(dbUser) : null;
  }

  async findActiveUsers(limit: number): Promise<User[]> {
    const users = await this.prisma.user.findMany({
      where: { isActive: true },
      take: limit,
      orderBy: { createdAt: 'desc' }
    });
    return users.map(u => this.mapToDomain(u));
  }

  async save(user: User): Promise<User> {
    const data = this.mapToPersistence(user);
    const saved = await this.prisma.user.upsert({
      where: { id: user.id },
      create: data,
      update: data
    });
    return this.mapToDomain(saved);
  }

  // Mapping entre modèle DB et entité domaine
  private mapToDomain(dbUser: any): User {
    return new User(
      dbUser.id,
      dbUser.email,
      dbUser.profile?.displayName
    );
  }

  private mapToPersistence(user: User): any {
    return {
      id: user.id,
      email: user.email,
      isActive: user.isActive
    };
  }
}

// Utilisation dans un service métier
class UserService {
  constructor(private userRepo: UserRepository) {}

  async activateUser(userId: string): Promise<void> {
    const user = await this.userRepo.findById(userId);
    if (!user) throw new Error('User not found');
    
    user.activate(); // Logique métier pure
    await this.userRepo.save(user);
  }
}

Mise en œuvre étape par étape

  1. Définir l'interface du repository avec des méthodes orientées métier (éviter les SELECT génériques)
  2. Créer les entités de domaine indépendantes du modèle de base de données
  3. Implémenter le repository concret avec la technologie de persistance choisie
  4. Ajouter les méthodes de mapping entre modèles DB et entités domaine
  5. Injecter le repository dans les services métier via l'inversion de dépendances
  6. Créer des repositories mock pour les tests unitaires
  7. Centraliser les patterns de requêtage (pagination, filtrage, tri) dans le repository

Conseil professionnel

Évitez le piège du Generic Repository qui expose des méthodes CRUD universelles (GetAll, GetById, etc.). Privilégiez des repositories spécifiques au domaine avec des méthodes métier explicites comme findOverdueInvoices() ou getTopSellingProducts(). Cela rend le code auto-documenté et empêche les fuites d'abstraction. Pour les opérations complexes, n'hésitez pas à créer des specifications pattern combinables.

Outils et frameworks associés

  • TypeORM : ORM avec support natif du Repository Pattern pour Node.js
  • Entity Framework (C#) : implémentation repository intégrée avec Unit of Work
  • Spring Data JPA : génération automatique de repositories via interfaces
  • Prisma : bien qu'étant un query builder, s'intègre facilement dans un repository pattern
  • MediatR : pour combiner repositories avec CQRS et pattern Mediator
  • AutoMapper : facilite le mapping entre entités domaine et modèles de persistance

Le Repository Pattern représente un investissement architectural qui paie sur le long terme en réduisant drastiquement la dette technique. En isolant la persistance, il permet aux équipes de migrer vers de nouvelles technologies de stockage (de PostgreSQL vers MongoDB par exemple) sans réécrire la logique métier. Pour les projets évolutifs, ce pattern est indispensable pour maintenir une architecture propre et testable. La clé du succès réside dans la définition d'interfaces métier riches plutôt que de simples wrappers techniques autour d'un ORM.

L'argentestdéjàsurlatable.

En 1 heure, découvrez exactement combien vous perdez et comment le récupérer.