* feat: Cal.diy — community-driven MIT-licensed fork of Cal.com This squashed commit contains all Cal.diy changes applied on top of calcom/cal.com main: - Rebrand Cal.com to Cal.diy across the entire codebase - Remove Enterprise Edition (EE) features, license checks, and AGPL restrictions - Switch license from AGPL-3.0 to MIT - Remove docs/ directory (migrated to Nextra at cal.diy) - Remove dead code: org tests, EE tips, platform nav, premium username, SAML/SSO, etc. - Clean up .env.example for self-hosted Cal.diy - Update Docker image references to calcom/cal.diy - Update README, CONTRIBUTING.md, and issue templates for Cal.diy community fork - Add PR welcome bot for Cal.diy contributors - Fix API v2 breaking changes oasdiff ignore entries - Replace Blacksmith CI runners with default GitHub Actions 3893 files changed, 20789 insertions(+), 411020 deletions(-) Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * refactor: remove org-specific /organizations/:orgId endpoints from API v2 atoms controllers (#1701) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix: revert Cal.diy Inc to Cal.com, Inc. in license files, copyright notices, and package metadata (#1702) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * rip out org related comments in api v2 --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
201 lines
5.3 KiB
Markdown
201 lines
5.3 KiB
Markdown
---
|
|
title: Repository Method Naming Conventions
|
|
impact: HIGH
|
|
impactDescription: Improves code discoverability and reusability
|
|
tags: 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.
|
|
|
|
```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) { ... }
|
|
}
|
|
```
|
|
|
|
### 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`.
|
|
|
|
```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 },
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
### 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.
|
|
|
|
```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 },
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
### Rule 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");
|
|
}
|
|
|
|
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.diy Engineering Blog](https://cal.com/blog/engineering-in-2026-and-beyond)
|