Skip to content

Architecture Overview

Understanding CodeNotify's Server architecture and design patterns.

System Architecture

mermaid
graph TB
    subgraph Client["Client Layer"]
        WebApp[Web App]
        MobileApp[Mobile App]
        ThirdParty[Third-party Services]
    end
    
    subgraph Gateway["API Gateway - NestJS"]
        Guards[Guards]
        Validation[Validation]
        Logging[Logging]
    end
    
    subgraph Application["Application Layer"]
        Auth[Auth Module]
        Users[Users Module]
        Contests[Contests Module]
        Notifications[Notifications Module]
        Platforms[Platforms Module]
    end
    
    subgraph Data["Data Layer"]
        MongoDB[(MongoDB Database)]
        ExternalAPIs[External APIs]
        Cache[Cache - Future]
    end
    
    Client -->|HTTP/REST| Gateway
    Gateway --> Application
    Application --> Data

Module Structure

Core Modules

1. Auth Module

Purpose: Authentication and authorization

Components:

  • AuthController - Signup, signin, refresh, signout endpoints
  • AuthService - JWT generation, password hashing
  • JwtStrategy - Passport JWT strategy
  • JwtAuthGuard - Route protection
  • RolesGuard - Role-based access control

Key Features:

  • JWT access tokens (15 min)
  • Refresh tokens (7 days)
  • Bcrypt password hashing
  • Role-based authorization

2. Users Module

Purpose: User management and profiles

Components:

  • UsersController - 8 endpoints (profile, list, role, delete)
  • UsersService - User CRUD operations
  • UserSchema - MongoDB schema with preferences
  • UserDto - Zod validation schemas

Key Features:

  • User preferences (platforms, notifications)
  • Account activation/deactivation
  • Role management (user/admin)
  • Profile customization

3. Contests Module

Purpose: Contest aggregation and management

Components:

  • ContestsController - 19 endpoints (CRUD, search, filter, sync)
  • ContestsService - Contest operations
  • ContestSchema - Multi-platform schema
  • ContestSchedulerService - Scheduled jobs

Key Features:

  • Multi-platform support (4 platforms)
  • Automatic sync (every 6 hours)
  • Full-text search
  • Advanced filtering
  • Analytics and statistics

4. Notifications Module

Purpose: Multi-channel notifications

Components:

  • NotificationsService - Notification logic
  • EmailService - Email via Resend
  • WhatsAppService - WhatsApp integration

Key Features:

  • Multi-channel (email, WhatsApp, push)
  • User preference-based
  • Scheduled checks (every 30 min)
  • Customizable timing

5. Platforms Module

Purpose: Platform adapter registry

Components:

  • PlatformAdapter - Interface
  • CodeforcesAdapter - Codeforces integration
  • LeetCodeAdapter - LeetCode GraphQL
  • CodeChefAdapter - CodeChef REST API
  • AtCoderAdapter - AtCoder integration

Key Features:

  • Adapter pattern
  • Unified data format
  • Retry logic
  • Health checks

Design Patterns

1. Adapter Pattern

Purpose: Abstract platform-specific implementations

typescript
interface PlatformAdapter {
  platformName: string;
  enabled: boolean;
  fetchContests(): Promise<ContestData[]>;
  healthCheck(): Promise<boolean>;
}

class CodeforcesAdapter implements PlatformAdapter {
  platformName = 'codeforces';
  enabled = true;
  
  async fetchContests(): Promise<ContestData[]> {
    // Codeforces-specific implementation
  }
}

Benefits:

  • Easy to add new platforms
  • Loose coupling
  • Testable in isolation

2. Dependency Injection

Purpose: Loose coupling and testability

typescript
@Injectable()
export class ContestsService {
  constructor(
    @InjectModel(Contest.name) private contestModel: Model<ContestDocument>,
    @Inject(PLATFORM_ADAPTERS) private adapters: PlatformAdapter[],
  ) {}
}

Benefits:

  • Testable with mocks
  • Flexible configuration
  • Clear dependencies

3. Repository Pattern

Purpose: Abstract data access

typescript
@Injectable()
export class UsersService {
  constructor(
    @InjectModel(User.name) private userModel: Model<UserDocument>,
  ) {}
  
  async findById(id: string): Promise<User> {
    return this.userModel.findById(id);
  }
}

Benefits:

  • Database agnostic
  • Centralized queries
  • Easy to test

4. Guard Pattern

Purpose: Route protection

typescript
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
@Delete(':id')
async deleteUser(@Param('id') id: string) {
  // Only admins can access
}

Benefits:

  • Declarative security
  • Reusable guards
  • Clear authorization

Data Flow

Authentication Flow

1. Client → POST /auth/signup
2. AuthService → Hash password
3. UsersService → Create user in DB
4. AuthService → Generate JWT tokens
5. Response → { user, accessToken, refreshToken }

Contest Sync Flow

1. Scheduler → Trigger sync (every 6 hours)
2. ContestsService → Get all platform adapters
3. For each adapter:
   a. Fetch contests from platform API
   b. Transform to internal format
   c. Upsert to MongoDB
4. Log results (synced, updated, failed)

Notification Flow

1. Scheduler → Check upcoming contests (every 30 min)
2. ContestsService → Find contests in notification window
3. For each contest:
   a. Get users with matching preferences
   b. Check if already notified
   c. Send via preferred channels
   d. Mark as notified

Database Schema

User Schema

typescript
{
  email: string (unique, indexed)
  password: string (hashed)
  name: string
  phoneNumber?: string
  role: 'user' | 'admin' (indexed)
  preferences: {
    platforms: string[]
    alertFrequency: string
    contestTypes: string[]
    notificationChannels: string[]
    notifyBefore: number
  }
  isActive: boolean (indexed)
  refreshToken?: string
  lastLogin?: Date
  createdAt: Date
  updatedAt: Date
}

Contest Schema

typescript
{
  platformId: string (indexed)
  name: string (indexed, text search)
  platform: enum (indexed)
  phase: enum (indexed)
  type: enum
  startTime: Date (indexed)
  endTime: Date
  durationMinutes: number
  description?: string (text search)
  websiteUrl?: string
  difficulty?: enum
  participantCount?: number
  problemCount?: number
  platformMetadata: object
  isActive: boolean (indexed)
  isNotified: boolean (indexed)
  lastSyncedAt?: Date
  createdAt: Date
  updatedAt: Date
}

Indexes:

  • { platformId: 1, platform: 1 } - Unique
  • { platform: 1, startTime: 1 } - Compound
  • { platform: 1, phase: 1 } - Compound
  • { name: 'text', description: 'text' } - Full-text

API Layers

1. Controller Layer

Responsibilities:

  • HTTP request handling
  • Input validation
  • Response formatting
  • Route protection
typescript
@Controller('contests')
export class ContestsController {
  @Get('upcoming')
  async getUpcoming(): Promise<ContestResponseDto[]> {
    return this.contestsService.findUpcoming();
  }
}

2. Service Layer

Responsibilities:

  • Business logic
  • Data transformation
  • External API calls
  • Error handling
typescript
@Injectable()
export class ContestsService {
  async findUpcoming(): Promise<Contest[]> {
    return this.contestModel.find({
      startTime: { $gt: new Date() },
      isActive: true
    }).sort({ startTime: 1 });
  }
}

3. Data Layer

Responsibilities:

  • Database operations
  • Schema definitions
  • Indexes and constraints
typescript
@Schema({ timestamps: true })
export class Contest {
  @Prop({ required: true })
  name: string;
  
  @Prop({ type: Date, required: true, index: true })
  startTime: Date;
}

Security Architecture

Authentication

  • JWT Access Tokens: Short-lived (15 min)
  • Refresh Tokens: Long-lived (7 days)
  • Password Hashing: Bcrypt with salt rounds
  • Token Storage: Secure httpOnly cookies (recommended)

Authorization

  • Role-Based: User vs Admin
  • Guard-Based: Declarative with decorators
  • Resource-Based: Owner-only access

Input Validation

  • Zod Schemas: Type-safe validation
  • DTO Classes: Request/response types
  • Pipe Validation: Automatic validation

Scalability Considerations

Horizontal Scaling

  • Stateless API: No session storage
  • JWT Tokens: Self-contained
  • Database Connection Pool: Configurable size
  • Load Balancer Ready: Health check endpoint

Performance Optimization

  • Database Indexes: Optimized queries
  • Pagination: Limit result sets
  • Caching: Future implementation
  • Async Operations: Non-blocking I/O

Monitoring

  • Logging: Structured logs
  • Health Checks: /contests/health
  • Error Tracking: Comprehensive error handling
  • Metrics: Future implementation

Technology Stack

Core

  • NestJS - Framework
  • TypeScript - Language
  • MongoDB - Database
  • Mongoose - ODM

Authentication

  • Passport - Auth middleware
  • JWT - Token format
  • Bcrypt - Password hashing

Validation

  • Zod - Schema validation
  • class-validator - DTO validation

Scheduling

  • @nestjs/schedule - Cron jobs
  • node-cron - Scheduler

HTTP Client

  • Axios - HTTP requests
  • @nestjs/axios - NestJS integration

Notifications

  • Resend - Email service

Best Practices

✅ Do

  1. Use DTOs for all requests/responses
  2. Validate all inputs with Zod
  3. Use guards for route protection
  4. Log important operations
  5. Handle errors gracefully
  6. Use indexes for queries
  7. Paginate large result sets
  8. Test all critical paths

❌ Don't

  1. Don't expose internal errors
  2. Don't skip validation
  3. Don't hardcode secrets
  4. Don't ignore security
  5. Don't block event loop
  6. Don't skip error handling

Built with ❤️ for competitive programmers worldwide.