_private/qwestly-docs/Engineering/code-organization-lib-vs-services.md
Table of Contents
Code Organizational - /lib vs /services
This document outlines the architectural patterns and organizational structure for the Qwestly codebase, with particular emphasis on the distinction between /lib and /services directories.
Directory Structure Overview
src/
├── app/ # Next.js App Router pages and layouts
├── components/ # Reusable UI components
├── services/ # Server-side business logic and API endpoints
├── lib/ # Client-side utilities and SDKs
├── types/ # TypeScript interface definitions
├── config/ # Application configuration and constants
└── middleware.ts # Next.js middleware
Core Architectural Principles
1. Server-Side vs Client-Side Processing
Server-Side Processing Required For:
- Data processing and calculations
- Database queries and aggregation
- Authentication and authorization
- API calls to external services
- Sensitive operations (payments, admin functions)
- SEO-critical data processing
Client-Side Processing Appropriate For:
- User interactions (button clicks, form inputs)
- Real-time updates (websockets)
- Animations and transitions
- Local UI state management
- Client-specific formatting (timezone, localization)
2. Single Responsibility Principle
Each component, service, and utility must have one clearly defined responsibility. This improves testability, maintainability, and reduces coupling.
Directory-Specific Guidelines
/services/ - Server-Side Business Logic
Purpose: Contains all server-side business logic, data processing, and external service integrations.
What Goes Here:
- Database operations and data access layer
- External API integrations (Auth0, LinkedIn, VAPI)
- Business logic and calculations
- Authentication and authorization services
- Background job processors
- LLM call abstractions
- Data transformation and processing
- Server-side data validation
Structure (for reference only, not exhaustive):
services/
├── auth/ # Authentication services
│ ├── auth0.service.ts # Auth0 management operations
│ ├── auth0.ts # Auth0 configuration
│ ├── profile.service.ts # User profile operations
│ └── waitlist.ts # Waitlist management
├── data/ # Data access layer
│ ├── background.ts # Background data operations
│ ├── candidates.ts # Candidate data operations
│ ├── users.ts # User data operations
│ └── linkedin-rapidapi.ts # LinkedIn API integration
└── analysis/ # Analysis services
├── index.ts # Analysis service exports
└── langsmith.ts # LangSmith integration
Examples:
// services/data/candidates.ts - Database operations
export async function getAllCandidates() {
const candidatesCollection = await mongodb.getCandidatesCollection();
return await candidatesCollection
.find({}, { projection: { _id: 0, candidate_id: 1, basic_info: 1 } })
.toArray();
}
// services/auth/auth0.service.ts - External service integration
export const getUsers = async (userIds: string[]) => {
const client = await getAuth0ManagementClient();
const { data } = await client.users.getAll({
q: `user_id:${userIds.join(" or ")}`,
});
return data;
};
/lib/ - Client-Side Utilities and SDKs
Purpose: Contains client-side utilities, SDKs, and helper functions that can run in the browser.
What Goes Here:
- Client-side API routing utilities
- Browser-specific utilities and polyfills
- Pure utility functions that can run in browser
- SDKs and client-side abstractions
- Client-side configuration and constants
- API request helpers
- Client-side data formatting utilities
Structure:
lib/
├── api/ # Client-side API utilities
│ ├── candidates.ts # Candidate API client functions
│ └── interviews.ts # Interview API client functions
├── api-router.ts # API routing utility
├── api-request.ts # HTTP request helpers
├── api-constants.ts # API endpoint constants
├── auth0-client.ts # Auth0 client configuration
├── mongodb.ts # MongoDB client utilities
├── mongodb_enhanced.ts # Enhanced MongoDB utilities
├── vapi-client.ts # VAPI client utilities
├── perplexity.ts # Perplexity API client
├── linkedin.ts # LinkedIn client utilities
├── konsole.ts # Console utilities
└── utils.ts # General utility functions
Examples:
// lib/api/candidates.ts - Client-side API calls
export async function getCandidate(candidateId: string): Promise<any | null> {
try {
const encodedId = encodeURIComponent(candidateId);
const response = await fetch(`/api/call-history/user/${encodedId}`);
// ... client-side processing
} catch (error) {
console.error("Error getting candidate:", error);
return null;
}
}
// lib/api-router.ts - Client-side routing utility
class ApiRouter {
private routes: Record<Method, Routes[]>;
// ... routing logic for client-side API calls
}
Key Distinctions: /lib vs /services
/services/ - Server-Side Only
Characteristics:
- Environment: Server-side only (Node.js)
- Purpose: Business logic, data processing, external integrations
- Access: Can access environment variables, databases, external APIs
- Security: Can handle sensitive operations and credentials
- Performance: Optimized for server-side execution
Use Cases:
- Database queries and data manipulation
- External API integrations (Auth0, LinkedIn, VAPI)
- Business logic and calculations
- Authentication and authorization
- Data transformation and processing
- Background job processing
/lib/ - Client-Side Compatible
Characteristics:
- Environment: Client-side compatible (browser-safe)
- Purpose: Utilities, SDKs, and helper functions
- Access: Limited to browser APIs and public endpoints
- Security: Cannot access sensitive credentials
- Performance: Optimized for client-side execution
Use Cases:
- API request helpers and routing
- Data formatting and validation
- Client-side utilities and polyfills
- SDKs for external services (client-side only)
- Browser-specific functionality
- Pure utility functions
Decision Framework
When to Use /services/
Use /services/ when the code:
- ✅ Needs to access databases or external APIs
- ✅ Requires environment variables or sensitive credentials
- ✅ Performs business logic or data processing
- ✅ Handles authentication or authorization
- ✅ Runs server-side only
- ✅ Integrates with external services (Auth0, LinkedIn, etc.)
When to Use /lib/
Use /lib/ when the code:
- ✅ Can run in the browser
- ✅ Provides utilities or helper functions
- ✅ Handles client-side API requests
- ✅ Formats or validates data on the client
- ✅ Provides SDKs for client-side use
- ✅ Contains pure functions with no side effects
Migration Guidelines
Moving Code Between Directories
From /lib/ to /services/:
- When code needs server-side access (databases, env vars)
- When code performs business logic
- When code handles sensitive operations
From /services/ to /lib/:
- When code can be client-side compatible
- When code provides pure utility functions
- When code handles client-side operations only
Refactoring Examples
// ❌ WRONG: Database operations in /lib/
// lib/candidates.ts
export async function getAllCandidates() {
const collection = await mongodb.getCandidatesCollection();
return await collection.find({}).toArray();
}
// ✅ CORRECT: Database operations in /services/
// services/data/candidates.ts
export async function getAllCandidates() {
const collection = await mongodb.getCandidatesCollection();
return await collection.find({}).toArray();
}
// ✅ CORRECT: Client-side API calls in /lib/
// lib/api/candidates.ts
export async function getCandidate(candidateId: string) {
const response = await fetch(`/api/candidates/${candidateId}`);
return response.json();
}
Best Practices
1. Clear Separation of Concerns
- Keep server-side logic in
/services/ - Keep client-side utilities in
/lib/ - Avoid mixing server and client concerns
2. Consistent Naming
- Use descriptive names that indicate purpose
- Follow established patterns within each directory
- Use consistent file extensions and naming conventions
3. Proper Imports
- Import from
/services/only in server-side code - Import from
/lib/in both client and server code - Avoid circular dependencies
4. Type Safety
- Define clear interfaces for all data structures
- Use TypeScript for better type safety
- Export types from appropriate directories
5. Error Handling
- Implement proper error handling in both directories
- Use consistent error patterns
- Log errors appropriately for each environment
Testing Considerations
Services Testing
- Unit test business logic in isolation
- Mock external dependencies
- Test database operations with test data
Lib Testing
- Test utilities with various inputs
- Mock browser APIs when necessary
- Test client-side error handling
Performance Considerations
Services
- Optimize database queries
- Implement proper caching strategies
- Use connection pooling for external services
Lib
- Minimize bundle size
- Use tree-shaking effectively
- Implement lazy loading where appropriate
Security Considerations
Services
- Validate all inputs
- Implement proper authentication
- Secure sensitive operations
- Use environment variables for secrets
Lib
- Never expose sensitive data
- Validate client-side inputs
- Implement proper error handling
- Use HTTPS for all API calls
This organizational structure ensures clear separation of concerns, improves maintainability, and follows Next.js best practices for server-side and client-side code organization.