d4ea931bf8
* feat(agents): add modular engineering rules from 2026 standards Add a rules directory with individual rule files derived from the Cal.com Engineering in 2026 and Beyond blog post. Rules are organized by section (architecture, quality, data, api, performance, testing, patterns, culture) following the Vercel agent-skills structure. Includes: - _sections.md defining rule categories and impact levels - _template.md for creating new rules - 14 individual rule files covering key engineering standards - README documenting the rules structure and usage Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * feat(agents): consolidate DI and Repository+DTO docs into rules - Move di-pattern.md content to rules/patterns-di-pattern.md - Extract Repository + DTO section from knowledge-base.md into: - rules/data-repository-methods.md (method naming conventions) - rules/data-dto-boundaries.md (DTO location and naming) - Update knowledge-base.md to reference the new rule files - Delete old di-pattern.md file Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * chore(agents): remove stub reference sections from knowledge-base.md The rules directory is self-contained with its own README, so these redirect sections are unnecessary clutter. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor(agents): combine DI pattern rules into single file Merged patterns-di-pattern.md into patterns-dependency-injection.md to eliminate overlap and create one comprehensive DI guide. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- 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.com Engineering Blog](https://cal.com/blog/engineering-in-2026-and-beyond)
|