_private/qwestly-docs/Engineering/code-organization-lib-vs-services.md

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.