Overview
PROVESA Web implements a clean separation between business logic (Services) and data access (Repositories). This pattern provides:- Testability - Business logic can be tested independently of database
- Maintainability - Changes to database queries don’t affect business logic
- Reusability - Services can orchestrate multiple repositories
- Type Safety - Drizzle ORM provides compile-time type checking
Architecture Pattern
Repository Layer
Purpose
Repositories provide a consistent API for database operations, abstracting away SQL and ORM implementation details.Standard Repository Pattern
Every repository exports an object with standard CRUD methods:Example: Products Repository
File:/src/lib/server/repositories/products.repository.js
Key Repository Patterns
1. Always Return Data
Repositories should return query results, not void:2. Use Type Inference
Drizzle provides type inference for insert/update:3. Sort by Default
Always include default sorting for consistency:4. Use Proper Operators
Drizzle provides type-safe operators:Advanced Repository Example: Concursos
File:/src/lib/server/repositories/concursos.repository.js
- Handles two related tables (
concursosandconcursosGanadores) - Provides specialized queries (
getActive,getGanadoresByConcurso) - Returns
nullfor not found instead of throwing
Service Layer
Purpose
Services contain business logic, validation, and orchestration. They:- Call one or more repositories
- Transform and validate data
- Handle file uploads
- Calculate derived values
- Enforce business rules
Standard Service Pattern
Example: Products Service
File:/src/lib/server/services/products.service.js
- Automatic
sortOrdercalculation - JSON parsing for JSONB fields
- Delegates database operations to repository
Advanced Service Example: Concursos
File:/src/lib/server/services/concursos.service.js
- Orchestrates multiple repositories (
concursosRepository,uploadRepository) - Handles file uploads to Cloudinary
- Extracts and validates FormData
- Provides public API (
getPublicData) and admin APIs - Manages related entities (contests and winners)
Common Patterns
Pattern 1: Auto-Incrementing Sort Order
Many tables use asortOrder field for manual ordering:
Pattern 2: File Upload Integration
Services handle file uploads, not repositories:Pattern 3: JSON Field Parsing
JSONB fields often come as strings from FormData:Pattern 4: Singleton Config Tables
Some tables should only have one row:Pattern 5: Parallel Data Loading
Services can orchestrate multiple repositories in parallel:Route Integration
Loading Data
Routes call services in theload function:
Form Actions
Routes call services in form actions:Testing Considerations
Repository Testing
Repositories can be tested against a real database:Service Testing
Services can be tested with mocked repositories:Best Practices
DO:
✅ Keep repositories simple - only database operations✅ Put business logic in services
✅ Use consistent naming (
{domain}.service.js, {domain}.repository.js)✅ Return typed results from repositories
✅ Handle errors in services, propagate to routes
✅ Use
Promise.all() for independent operations✅ Document complex business logic with comments
DON’T:
❌ Put business logic in repositories❌ Call repositories directly from routes
❌ Mix database queries with file uploads in repositories
❌ Ignore type safety from Drizzle
❌ Swallow errors silently
❌ Use raw SQL unless absolutely necessary
Next Steps
- Database Schema - Table definitions
- Architecture Overview - System design
- Project Structure - File organization
