From 7af46cc7ab26ac7d5d92cc0969c86a844a8b43b9 Mon Sep 17 00:00:00 2001 From: Hariom Balhara Date: Wed, 15 Oct 2025 19:14:06 +0530 Subject: [PATCH] docs: add repository method conventions to knowledge base (#24443) - Add comprehensive repository method naming conventions - Include examples for method naming patterns - Document separation of concerns between repositories and services - Emphasize reusability and generic method naming Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .agents/knowledge-base.md | 218 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) diff --git a/.agents/knowledge-base.md b/.agents/knowledge-base.md index 11d1987d92..ce55849012 100644 --- a/.agents/knowledge-base.md +++ b/.agents/knowledge-base.md @@ -306,6 +306,224 @@ import { Button } from "@calcom/ui/components/button"; - **Tests**: Same as source file + `.test.ts` or `.spec.ts` - **Avoid**: Dot-suffixes like `.service.ts`, `.repository.ts` (except for tests, types, specs) +## Repository Method Conventions + +Repositories should follow consistent naming and design patterns to promote reusability and maintainability. + +### Method Naming Rules + +**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. + +```typescript +// ✅ 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) { ... } +} +``` + +**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`. + +```typescript +// ✅ 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, + }, + }); + } +} +``` + +**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. + +```typescript +// ✅ 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, + }, + }); + } +} +``` + +**4. No business logic in repositories** + +Repositories should only handle data access. Business logic, validations, and complex transformations belong in the Service layer. + +```typescript +// ✅ 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"); + } + + // Business logic: send confirmation emails + await this.emailService.sendConfirmationToAttendees(booking.attendees); + + // Update status through repository + 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"); + } + + // ❌ Business logic shouldn't be here + 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 + ## When using Day.js ```typescript