These standards govern all backend development in Specboot-managed projects. The backend follows Domain-Driven Design (DDD) principles and a layered architecture to ensure consistency, maintainability, and scalability. They apply to any AI agent working across theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/LIDR-academy/lidr-specboot/llms.txt
Use this file to discover all available pages before exploring further.
backend/src/, backend/prisma/, and related configuration files.
Technology Stack
Core Technologies
| Technology | Purpose |
|---|---|
| Node.js | Runtime environment |
| TypeScript (strict mode) | Type-safe development |
| Express.js | Web application framework |
| Prisma | Modern ORM for database access |
| PostgreSQL | Relational database (Docker container) |
Testing & Tooling
- Jest — testing framework with TypeScript support; 90% coverage threshold across branches, functions, lines, and statements
- ESLint — code linting
- Serverless Framework — AWS Lambda deployment support
Architecture Overview
Domain-Driven Design (DDD)
DDD centers development around business logic and domain knowledge, producing models that accurately reflect real business rules. The key benefits are a common language between developers and domain experts, clear domain boundaries, and high maintainability through subdomain isolation.Layered Architecture
| Layer | Path | Responsibility |
|---|---|---|
| Presentation | src/presentation/ | Controllers handle HTTP requests/responses; routes define API endpoints |
| Application | src/application/ | Services contain business logic and orchestration; validator.ts handles input validation |
| Domain | src/domain/ | Models define core business entities; repository interfaces define data access contracts |
| Infrastructure | src/infrastructure/ | Prisma ORM implementations; logger; Prisma client setup |
Project Structure
Domain-Driven Design Principles
Entities
Entities are objects with a distinct identity that persists over time. Use classes to encapsulate business logic and maintain consistency of internal state.Candidate is an entity because its unique id distinguishes it from all other candidates, even if all other properties are identical.
Value Objects
Value objects describe aspects of the domain without conceptual identity — they are defined by their attributes, not an identifier.Classes like
Education and WorkExperience currently carry unique identifiers, making them entities in practice. Consider removing those identifiers — or embedding them inside the Candidate document — if using a NoSQL store where they should behave as true value objects.Aggregates
Aggregates are clusters of objects treated as a unit, with a root entity that enforces invariants and consistency boundaries.Candidate is the aggregate root that contains Education, WorkExperience, Resume, and Application. Operations affecting nested objects must be handled through the aggregate root to maintain integrity.
Repositories
Repositories provide interfaces for accessing aggregates and entities, fully encapsulating data access logic.Domain Services
Domain services contain business logic that doesn’t naturally belong to an entity or value object.Additional DDD Recommendations
- Factories — Use factory methods to encapsulate the creation of complex aggregates that require specific initial state conforming to business rules.
- Relationship modeling — Regularly review entity relationships to ensure they reflect actual domain needs. Remove unnecessary relationships and add any that facilitate real business operations.
- Domain events — Implement a domain event system so entities can publish events that other components handle without tight coupling.
SOLID and DRY Principles
Single Responsibility Principle (SRP)
Each class should have exactly one reason to change. Separate validation from persistence:Open/Closed Principle (OCP)
Extend functionality through subclasses rather than modifying existing classes:Liskov Substitution Principle (LSP)
Objects of a derived class should be replaceable with objects of the base class without altering the program’s functionality.Interface Segregation Principle (ISP)
Prefer many small interfaces over a single large one:Dependency Inversion Principle (DIP)
High-level modules must depend on abstractions, not concrete implementations. InjectPrismaClient through constructors rather than instantiating it inside domain classes.
DRY (Don’t Repeat Yourself)
Centralize repeated logic in shared methods or base classes. For example, email validation should live in a singlevalidateEmail() method on Candidate and be called from both save() and update(), never duplicated inline.
Coding Standards
Naming Conventions
| Artifact | Convention | Example |
|---|---|---|
| Variables & functions | camelCase | candidateId, findCandidateById |
| Classes & interfaces | PascalCase | Candidate, CandidateRepository |
| Constants | UPPER_SNAKE_CASE | MAX_CANDIDATES_PER_PAGE |
| Types & interfaces | PascalCase | CandidateData, ICandidateRepository |
| Files | camelCase | candidateService.ts, candidateController.ts |
TypeScript Usage
- Enable strict mode in
tsconfig.json - Use explicit types for all function parameters and return values
- Define interfaces for complex data structures
- Avoid
any— useunknownor specific types instead
Error Handling
Create domain-specific error classes, use global error middleware for consistent responses, and propagate errors naturally through the call stack.Validation Patterns
Validate all inputs at the application layer using the centralizedsrc/application/validator.ts. Always validate before executing business logic.
Logging Standards
Use the centralized logger fromsrc/infrastructure/logger.ts, appropriate log levels (info, error, warn, debug), and structured log messages with relevant context.
API Design Standards
REST Endpoints
Request/Response Patterns
All endpoints return JSON with a consistent envelope:Error Response Format
Map errors to appropriate HTTP status codes:CORS Configuration
Configure CORS to allow only specific origins in production:Database Patterns
Prisma Schema
prisma/schema.prisma is the single source of truth for database structure. Use consistent naming (camelCase for fields, PascalCase for models) and define relationships using Prisma relation syntax.
Migrations
All database changes must be version-controlled through migrations:Repository Pattern
Define interfaces in the domain layer; implement them with Prisma in the infrastructure layer; inject the Prisma client via constructor:Testing Standards
The project enforces a 90% coverage threshold for branches, functions, lines, and statements. Tests must pass before every merge. Coverage reports are stored in
coverage/ with the format YYYYMMDD-backend-coverage.md.Test File Structure
- Use descriptive file names:
[componentName].test.ts - Place test files alongside the source code they test
- Use Jest with TypeScript support
Test Organization Pattern (AAA)
Always follow the Arrange-Act-Assert pattern:Test Case Naming
Use behavior-driven naming:should_[expected_behavior]_when_[condition]. Group related cases under descriptive describe blocks.
Test Coverage Categories
Every function needs coverage across five categories:- Happy Path — valid inputs producing expected outputs
- Error Handling — invalid inputs, missing data, database errors
- Edge Cases — boundary values, null/undefined inputs, empty data
- Validation — input validation and business rule enforcement
- Integration Points — external service calls and database operations
Mocking Standards
- Mock all external dependencies (models, services, database clients)
- Use
jest.mock()at the top of test files - Clear all mocks in
beforeEach()to ensure test isolation - Create mock instances with realistic data structures
Layer-Specific Testing
| Layer | What to Mock | What to Test |
|---|---|---|
| Controller | Service layer | HTTP request/response handling, parameter parsing, error formatting |
| Service | Domain models and repositories | Business logic, data transformation, error handling |
| Database | Prisma client | Correct queries/parameters, transaction handling |
Async Testing
- Always use
async/awaitfor asynchronous operations - Use
Promise.allSettled()for testing concurrent operations - Properly handle promise rejections
- Test timeout scenarios where applicable
Error Testing
- Test both expected errors and unexpected errors
- Verify error messages are descriptive and helpful
- Test error propagation through service layers
- Ensure proper HTTP status codes in controller tests
Controller Testing Specifics
- Mock the service layer completely
- Test HTTP request/response handling
- Verify parameter parsing and validation
- Test error response formatting
- Use realistic Express Request/Response mocks
Service Testing Specifics
- Mock domain models and repositories
- Test business logic in isolation
- Verify data transformation and validation
- Test error handling and edge cases
- Mock external dependencies (Prisma, validators)
Database Testing
- Mock Prisma client and all database operations
- Test both successful and failed database operations
- Verify correct database queries and parameters
- Test transaction handling and rollback scenarios
Test Data Management
- Use factory functions for creating test data
- Keep test data consistent and realistic
- Avoid hardcoded values in multiple places
- Use meaningful test data that reflects real-world scenarios
Code Quality Standards
- Use strict typing for all test parameters and return values
- Define proper interfaces for mock data
- Use type assertions sparingly and with proper justification
- Write clear, descriptive test names that explain the scenario
- Add comments for complex test setups
- Keep test code as readable as production code
- Keep tests fast and focused; avoid unnecessary async operations
- Use appropriate mock strategies to avoid real I/O
Integration with Development Workflow
- Run tests before every commit
- Ensure all tests pass before merging
- Use test-driven development when appropriate
- Update tests when modifying existing functionality
Common Anti-Patterns to Avoid
- Don’t test implementation details — test behavior
- Don’t create overly complex test setups
- Don’t ignore failing tests or skip error scenarios
- Don’t use real database connections in unit tests
- Don’t create tests that depend on external services
- Don’t write tests tightly coupled to implementation details
Performance Best Practices
Database Query Optimization
Avoid N+1 queries by using Prisma’sinclude:
Async/Await Patterns
UsePromise.all() for independent parallel operations:
Error Handling Performance
- Early Returns — Return early to avoid unnecessary processing.
- Error Propagation — Let errors propagate naturally through the call stack.
- Avoid Over-Wrapping — Don’t wrap errors unnecessarily; let the global error middleware handle them.
Security Best Practices
Input Validation
Validate and sanitize all user inputs before processing. Use TypeScript types and the centralized validator to enforce type safety.Environment Variables
Never commit.env files or secrets to version control. Validate required environment variables at startup:
Dependency Injection
Inject the Prisma client via Express middleware to avoid global state and improve testability:Development Workflow
Git Workflow
- Feature branches with clear, descriptive names to enable parallel work and avoid conflicts
- Descriptive commit messages in English
- Code review before merging
- Small, focused branches — one feature or fix per branch
Development Scripts
Code Quality Gates
Before deploying: ESLint passes, TypeScript compiles without errors, all tests pass at 90%+ coverage.Serverless Deployment
The backend supports AWS Lambda deployment via the Serverless Framework.- Lambda entry point:
src/lambda.ts - Configuration:
serverless.yml - Build command:
npm run build:lambda