AI Development GuideDevelopment
Code Patterns
Exact code patterns to follow - no variations allowed
Code Patterns
These are the exact patterns to follow. AI assistants must use these patterns without modification.
Service Pattern
Structure:
- Injectable class with constructor injection
- Methods take tenantId as first parameter
- Return domain objects (not DTOs)
- Throw NestJS exceptions for errors
Simple Rule:
- WRITES (create, update, delete) → Always use Service layer
- READS (get, list) → Can use Repository directly
Why this rule?
- Write operations often need validation, audit logging, events
- Starting with Service layer makes it easy to add logic later
- Read operations rarely need business logic
When Service is REQUIRED:
- Business logic or validation
- Multiple repository calls
- External service calls
- Complex transactions
- Any write operation
When Repository direct is OK:
- Simple read operations (get by ID, list with filters)
- No business logic needed
Controller Pattern
Structure:
- Controller class with route prefix
- Use guards:
@UseGuards(TenantGuard)(MVP uses session-based auth via Auth.js, not JWT) - Use decorators:
@TenantId()or@Tenant()for tenant context - Use
@RequirePermissions()for permission checks - Return
{ data, error }format
Simple Pattern (Controller → Repository):
- For simple CRUD without business logic
- Direct repository calls
- No service layer
Full Pattern (Controller → Service → Repository):
- For operations with business logic
- Service handles validation and coordination
- Repository handles data access
Repository Pattern
Structure:
- All methods take tenantId as first parameter
- Always filter by tenantId in queries
- Use Prisma client directly
- Return domain objects
Required:
- Tenant filtering on every query
- Soft delete support (deletedAt field)
- Standard CRUD methods: findAll, findOne, create, update, remove
Response Format
Standard Response:
{
"data": { ... },
"error": null
}Error Response:
{
"data": null,
"error": "Error message"
}Permission Pattern
Format: resource:action:scope
Examples:
employees:read:departmenttime_off:approve:alldocuments:delete:own
Usage:
- Decorator:
@RequirePermissions('employees:read:department') - Service:
PermissionService.hasPermission(userId, tenantId, resource, action, scope)
Error Handling Pattern
Use NestJS exceptions:
NotFoundException- Resource not foundBadRequestException- Invalid inputUnauthorizedException- Auth failureForbiddenException- Permission deniedConflictException- Resource conflict
Pattern:
- Throw exceptions in services
- Let NestJS exception filter handle responses
- Return standard error format
DTO Pattern
Structure:
- Use class-validator decorators
- Use class-transformer for serialization
- Validate in controller with ValidationPipe
Naming:
CreateEntityDto- For creationUpdateEntityDto- For updates (all fields optional)EntityResponseDto- For responses (if needed)
Database Pattern
Prisma Usage:
- Use Prisma 5.x (NOT 6.x)
- All IDs are UUIDs (NOT auto-increment)
- All models have createdAt, updatedAt
- Tenant isolation via tenantId field
Queries:
- Always include tenantId in where clause
- Use transactions for multi-step operations
- Use soft deletes (deletedAt field)
Module Pattern
Structure:
- One module per feature
- Import PrismaModule
- Export service (if used by other modules)
- Register controllers and providers
Testing Pattern
Unit Tests:
- Test services with mocked repositories
- Test repositories with mocked Prisma
- Test business logic in isolation
Integration Tests:
- Test full request flow
- Use test database
- Clean up after tests
Anti-Patterns (DO NOT USE)
- ❌ Repository pattern abstractions (use Prisma directly)
- ❌ Generic base classes (copy-paste instead)
- ❌ Complex inheritance hierarchies
- ❌ Event-driven architecture (use direct calls)
- ❌ GraphQL (use REST only)
- ❌ Microservices (use monolith)
Test-Driven Development Pattern (Optional)
When TDD is appropriate:
- Complex business logic
- Edge cases are critical
- Refactoring existing code
TDD Workflow with AI
Step 1: Write Tests First
"Write tests for [feature] based on these expected behaviors:
- Input X should return Y
- Input A should throw error B
DO NOT write implementation code yet."Step 2: Verify Tests Fail
"Run the tests and confirm they fail.
Show me the failure output."Step 3: Implement
"Now implement the code to make tests pass.
DO NOT modify the tests."Step 4: Verify Tests Pass
"Run tests again. All should pass.
If any fail, fix implementation (not tests)."When NOT to Use TDD
- Simple CRUD operations
- UI components (use visual testing)
- Configuration changes
- Prototyping/exploration
Gate Integration
For steps requiring TDD:
Gate: All tests pass (`npm test [file]` returns 0 failures)