Services and Repositories
This skill describes how to structure business logic (services) and data access (repositories).
1. Service layer (business rules)
Services should:
- Enforce business rules
- Orchestrate multiple repositories
- Avoid HTTP-specific types and concerns
Example:
export class UserService {
constructor(private readonly userRepo: UserRepository) {}
async create(input: CreateUserInput): Promise<User> {
const existing = await this.userRepo.findByEmail(input.email);
if (existing) throw new ConflictError("Email already exists");
return await this.userRepo.create(input);
}
}
2. Repository layer (data access)
Repositories should:
- Encapsulate query construction
- Hide ORM details from services
- Provide transaction helpers where needed
export class UserRepository {
constructor(private readonly db: PrismaClient) {}
findByEmail(email: string) {
return this.db.user.findUnique({ where: { email } });
}
create(input: CreateUserInput) {
return this.db.user.create({ data: input });
}
}
3. When to introduce repositories
Use repositories when:
- Queries are non-trivial (joins, pagination, filtering)
- Multiple call sites reuse the same query logic
- You need better test seams and isolation
If a service is a thin wrapper over a single simple query, a repository may be optional.
Related Skills
database-patternstesting-guide
Converted and distributed by TomeVault — claim your Tome and manage your conversions.