Skip to content

Latest commit

 

History

History
200 lines (164 loc) · 5.29 KB

File metadata and controls

200 lines (164 loc) · 5.29 KB
title impact impactDescription tags
Repository Method Naming Conventions
HIGH
Improves code discoverability and reusability
data, repository, naming, conventions, methods

Repository Method Naming Conventions

Impact: HIGH

Repository methods should follow consistent naming conventions to improve discoverability and promote code reuse across different features.

Rule 1: Don't include the repository's entity name in method names

Method names should be concise and avoid redundancy since the repository class name already indicates the entity type.

// Good - Concise method names
class BookingRepository {
  findById(id: string) { ... }
  findByUserId(userId: string) { ... }
  create(data: BookingCreateInput) { ... }
  delete(id: string) { ... }
}

// Bad - Redundant entity name in methods
class BookingRepository {
  findBookingById(id: string) { ... }
  findBookingByUserId(userId: string) { ... }
  createBooking(data: BookingCreateInput) { ... }
  deleteBooking(id: string) { ... }
}

Rule 2: Use include or similar keywords for methods that fetch relational data

When a method retrieves additional related entities, make this explicit in the method name using keywords like include, with, or andRelations.

// Good - Clear indication of included relations
class EventTypeRepository {
  findById(id: string) {
    return prisma.eventType.findUnique({
      where: { id },
    });
  }

  findByIdIncludeHosts(id: string) {
    return prisma.eventType.findUnique({
      where: { id },
      include: { hosts: true },
    });
  }

  findByIdIncludeHostsAndSchedule(id: string) {
    return prisma.eventType.findUnique({
      where: { id },
      include: { hosts: true, schedule: true },
    });
  }
}

// Bad - Unclear what data is included
class EventTypeRepository {
  findById(id: string) {
    return prisma.eventType.findUnique({
      where: { id },
      include: { hosts: true, schedule: true },
    });
  }

  findByIdForReporting(id: string) {
    return prisma.eventType.findUnique({
      where: { id },
      include: { hosts: true },
    });
  }
}

Rule 3: Keep methods generic and reusable - avoid use-case-specific names

Repository methods should be general-purpose and describe what data they return, not how or where it's used. This promotes code reuse across different features.

// Good - Generic, reusable methods
class BookingRepository {
  findByUserIdIncludeAttendees(userId: string) {
    return prisma.booking.findMany({
      where: { userId },
      include: { attendees: true },
    });
  }

  findByDateRangeIncludeEventType(startDate: Date, endDate: Date) {
    return prisma.booking.findMany({
      where: {
        startTime: { gte: startDate },
        endTime: { lte: endDate },
      },
      include: { eventType: true },
    });
  }
}

// Bad - Use-case-specific method names
class BookingRepository {
  findBookingsForReporting(userId: string) {
    return prisma.booking.findMany({
      where: { userId },
      include: { attendees: true },
    });
  }

  findBookingsForDashboard(startDate: Date, endDate: Date) {
    return prisma.booking.findMany({
      where: {
        startTime: { gte: startDate },
        endTime: { lte: endDate },
      },
      include: { eventType: true },
    });
  }
}

Rule 4: No business logic in repositories

Repositories should only handle data access. Business logic, validations, and complex transformations belong in the Service layer.

// Good - Repository only handles data access
class BookingRepository {
  findByIdIncludeAttendees(id: string) {
    return prisma.booking.findUnique({
      where: { id },
      include: { attendees: true },
    });
  }

  updateStatus(id: string, status: BookingStatus) {
    return prisma.booking.update({
      where: { id },
      data: { status },
    });
  }
}

class BookingService {
  async confirmBooking(bookingId: string) {
    const booking = await this.bookingRepository.findByIdIncludeAttendees(bookingId);
    
    if (!booking) {
      throw new Error("Booking not found");
    }

    if (booking.status !== "PENDING") {
      throw new Error("Only pending bookings can be confirmed");
    }

    await this.emailService.sendConfirmationToAttendees(booking.attendees);
    return this.bookingRepository.updateStatus(bookingId, "CONFIRMED");
  }
}

// Bad - Business logic in repository
class BookingRepository {
  async confirmBooking(bookingId: string) {
    const booking = await prisma.booking.findUnique({
      where: { id: bookingId },
      include: { attendees: true },
    });

    if (!booking) {
      throw new Error("Booking not found");
    }

    if (booking.status !== "PENDING") {
      throw new Error("Only pending bookings can be confirmed");
    }

    await sendEmailToAttendees(booking.attendees);

    return prisma.booking.update({
      where: { id: bookingId },
      data: { status: "CONFIRMED" },
    });
  }
}

Summary

  • Method names should be concise: findById not findBookingById
  • Use include/with keywords when fetching relations: findByIdIncludeHosts
  • Keep methods generic and reusable: findByUserIdIncludeAttendees not findBookingsForReporting
  • No business logic in repositories - that belongs in Services

Reference: Cal.com Engineering Blog