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 --> DataModule Structure
Core Modules
1. Auth Module
Purpose: Authentication and authorization
Components:
AuthController- Signup, signin, refresh, signout endpointsAuthService- JWT generation, password hashingJwtStrategy- Passport JWT strategyJwtAuthGuard- Route protectionRolesGuard- 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 operationsUserSchema- MongoDB schema with preferencesUserDto- 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 operationsContestSchema- Multi-platform schemaContestSchedulerService- 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 logicEmailService- Email via ResendWhatsAppService- 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- InterfaceCodeforcesAdapter- Codeforces integrationLeetCodeAdapter- LeetCode GraphQLCodeChefAdapter- CodeChef REST APIAtCoderAdapter- 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 notifiedDatabase 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
- Use DTOs for all requests/responses
- Validate all inputs with Zod
- Use guards for route protection
- Log important operations
- Handle errors gracefully
- Use indexes for queries
- Paginate large result sets
- Test all critical paths
❌ Don't
- Don't expose internal errors
- Don't skip validation
- Don't hardcode secrets
- Don't ignore security
- Don't block event loop
- Don't skip error handling

