Technical Architecture
System Overview
Oddxer is built as a modern, scalable microservices-ready application with real-time capabilities, secure authentication, and optimized database performance.
mermaid
graph TB
subgraph "Client Layer"
A[Web App - React 18]
B[Mobile App - React Native]
C[Admin Dashboard]
end
subgraph "API Gateway"
D[Express.js Server]
E[Socket.io Server]
end
subgraph "Business Logic"
F[Auth Service]
G[Code Service]
H[Wallet Service]
I[Vote Service]
J[Leaderboard Service]
end
subgraph "Data Layer"
K[(MongoDB)]
L[(Redis Cache)]
end
subgraph "External Services"
M[SMS Provider]
N[Mobile Money APIs]
O[Bookmaker APIs]
end
A --> D
B --> D
C --> D
A --> E
B --> E
D --> F
D --> G
D --> H
D --> I
D --> J
F --> K
G --> K
H --> K
I --> K
J --> K
F --> L
G --> L
F --> M
H --> N
I --> O
style A fill:#4CAF50
style B fill:#4CAF50
style C fill:#4CAF50
style D fill:#2196F3
style E fill:#FF9800
style K fill:#9C27B0Technology Stack
Backend
- Runtime: Node.js 18+
- Framework: Express.js 4.x
- Language: TypeScript 5.x
- Real-time: Socket.io 4.x
- Authentication: JWT (jsonwebtoken)
- Validation: Joi
- Password Hashing: bcryptjs
Database
- Primary Database: MongoDB 6.x
- ODM: Mongoose 8.x
- Caching: Redis 7.x (planned)
- Indexing: Compound indexes for performance
Frontend
- Framework: React 18
- Build Tool: Vite 5.x
- Styling: TailwindCSS 3.x
- State Management: Context API / Redux (planned)
- HTTP Client: Axios / Fetch API
- WebSocket: Socket.io Client
DevOps
- Version Control: Git
- CI/CD: GitHub Actions (planned)
- Containerization: Docker (planned)
- Hosting: VPS / Cloud (AWS/DigitalOcean)
- Monitoring: Grafana + Prometheus (planned)
Application Architecture
Layered Architecture
┌─────────────────────────────────────┐
│ Presentation Layer │
│ (Routes, Controllers, Middleware) │
├─────────────────────────────────────┤
│ Business Logic Layer │
│ (Services) │
├─────────────────────────────────────┤
│ Data Access Layer │
│ (Models, Repositories) │
├─────────────────────────────────────┤
│ Database Layer │
│ (MongoDB, Redis) │
└─────────────────────────────────────┘Directory Structure
oddxer-api/
├── src/
│ ├── app.ts # Express app configuration
│ ├── server.ts # Server entry point
│ ├── config/
│ │ ├── database.ts # MongoDB connection
│ │ ├── socket.ts # Socket.io setup
│ │ └── jwt.ts # JWT configuration
│ ├── controllers/
│ │ ├── auth.controller.ts
│ │ ├── code.controller.ts
│ │ ├── wallet.controller.ts
│ │ ├── vote.controller.ts
│ │ └── leaderboard.controller.ts
│ ├── services/
│ │ ├── auth.service.ts
│ │ ├── code.service.ts
│ │ ├── wallet.service.ts
│ │ ├── vote.service.ts
│ │ └── leaderboard.service.ts
│ ├── models/
│ │ ├── user.model.ts
│ │ ├── betting-code.model.ts
│ │ ├── wallet.model.ts
│ │ ├── transaction.model.ts
│ │ ├── vote.model.ts
│ │ └── leaderboard.model.ts
│ ├── routes/
│ │ ├── auth.routes.ts
│ │ ├── code.routes.ts
│ │ ├── wallet.routes.ts
│ │ ├── vote.routes.ts
│ │ └── leaderboard.routes.ts
│ ├── middleware/
│ │ ├── auth.ts # JWT authentication
│ │ ├── validate.ts # Joi validation
│ │ ├── errorHandler.ts # Global error handler
│ │ └── rateLimiter.ts # Rate limiting
│ ├── validators/
│ │ ├── auth.validator.ts
│ │ ├── code.validator.ts
│ │ ├── wallet.validator.ts
│ │ └── vote.validator.ts
│ ├── utils/
│ │ ├── ApiResponse.ts # Standardized responses
│ │ ├── ApiError.ts # Custom error class
│ │ └── generateOtp.ts # OTP generation
│ └── types/
│ └── index.ts # TypeScript interfaces
├── package.json
├── tsconfig.json
└── .envDatabase Schema Design
Entity Relationship Diagram
mermaid
erDiagram
User ||--o{ BettingCode : creates
User ||--|| Wallet : has
User ||--o{ Transaction : makes
User ||--o{ Vote : casts
User ||--|| Leaderboard : has
BettingCode ||--o| User : soldTo
BettingCode ||--o{ Vote : receives
BettingCode ||--o{ Transaction : generates
Wallet ||--o{ Transaction : contains
User {
ObjectId _id PK
string phoneNumber UK
string username UK
string email
enum role
boolean isVerified
boolean isActive
string otp
date otpExpires
array refreshTokens
date createdAt
date updatedAt
}
BettingCode {
ObjectId _id PK
ObjectId seller FK
enum sport
string title
string description
number odds
string bookmaker
number price
enum status
string code
date expiresAt
ObjectId soldTo FK
date soldAt
date finalizedAt
number winVotes
number lossVotes
number totalVotes
date createdAt
date updatedAt
}
Wallet {
ObjectId _id PK
ObjectId user FK
number balance
string currency
date createdAt
date updatedAt
}
Transaction {
ObjectId _id PK
ObjectId user FK
enum type
number amount
string currency
enum paymentMethod
enum status
string reference UK
ObjectId bettingCode FK
object metadata
date createdAt
date updatedAt
}
Vote {
ObjectId _id PK
ObjectId bettingCode FK
ObjectId voter FK
enum voteType
date createdAt
}
Leaderboard {
ObjectId _id PK
ObjectId seller FK
number totalSales
number totalWins
number totalLosses
number winRate
number averageROI
number totalRevenue
number rank
date updatedAt
}Database Indexes
Optimized indexes for high-performance queries:
User Collection
javascript
db.users.createIndex({ phoneNumber: 1 }) // Login lookup
db.users.createIndex({ username: 1 }) // Unique username
db.users.createIndex({ role: 1 }) // Role filtering
db.users.createIndex({ isActive: 1 }) // Active usersBettingCode Collection
javascript
db.bettingcodes.createIndex({ seller: 1, status: 1 }) // Seller's codes
db.bettingcodes.createIndex({ sport: 1, status: 1 }) // Sport filtering
db.bettingcodes.createIndex({ status: 1, sport: 1, price: 1 }) // Marketplace query
db.bettingcodes.createIndex({ price: 1 }) // Price sorting
db.bettingcodes.createIndex({ expiresAt: 1 }) // Expiry checks
db.bettingcodes.createIndex({ createdAt: -1 }) // Recent codes
db.bettingcodes.createIndex({ soldTo: 1 }) // Buyer's purchasesTransaction Collection
javascript
db.transactions.createIndex({ user: 1, type: 1 }) // User's transaction type
db.transactions.createIndex({ user: 1, status: 1 }) // User's pending transactions
db.transactions.createIndex({ reference: 1 }) // Reference lookup
db.transactions.createIndex({ createdAt: -1 }) // Recent transactionsVote Collection
javascript
db.votes.createIndex({ bettingCode: 1, voter: 1 }, { unique: true }) // One vote per user
db.votes.createIndex({ bettingCode: 1, voteType: 1 }) // Vote counting
db.votes.createIndex({ voter: 1 }) // User's votesLeaderboard Collection
javascript
db.leaderboards.createIndex({ seller: 1 }) // Seller lookup
db.leaderboards.createIndex({ winRate: -1 }) // Win rate ranking
db.leaderboards.createIndex({ totalSales: -1 }) // Sales ranking
db.leaderboards.createIndex({ totalRevenue: -1 }) // Revenue ranking
db.leaderboards.createIndex({ winRate: -1, totalSales: -1, totalRevenue: -1 }) // Composite ranking
db.leaderboards.createIndex({ rank: 1 }) // Rank lookupAuthentication Flow
mermaid
sequenceDiagram
participant U as User
participant C as Client App
participant A as API Server
participant DB as Database
participant SMS as SMS Provider
Note over U,SMS: Registration Flow
U->>C: Enter phone + username
C->>A: POST /auth/register
A->>DB: Check if user exists
DB-->>A: Not exists
A->>A: Generate 6-digit OTP
A->>DB: Save user with OTP
A->>SMS: Send OTP to phone
SMS-->>U: SMS with OTP
A-->>C: 201 Created (OTP sent)
Note over U,SMS: Verification Flow
U->>C: Enter OTP
C->>A: POST /auth/verify-otp
A->>DB: Validate OTP
DB-->>A: OTP valid
A->>A: Generate JWT tokens
A->>DB: Update user.isVerified = true
A->>DB: Store refresh token (hashed)
A-->>C: Access + Refresh tokens
C->>C: Store tokens
Note over U,SMS: Authenticated Request
C->>A: GET /wallet (Bearer token)
A->>A: Verify JWT signature
A->>A: Check token expiry
A->>DB: Fetch user data
DB-->>A: User data
A-->>C: Wallet data
Note over U,SMS: Token Refresh
C->>A: POST /auth/refresh
A->>DB: Validate refresh token
DB-->>A: Token valid
A->>A: Generate new access token
A->>DB: Rotate refresh token
A-->>C: New tokensReal-Time Architecture
Socket.io Event Flow
mermaid
graph LR
A[User Action] --> B[API Endpoint]
B --> C[Service Layer]
C --> D[Database Update]
D --> E[Emit Socket Event]
E --> F[Socket.io Server]
F --> G[Connected Clients]
G --> H[UI Update]
style E fill:#FF9800
style F fill:#FF9800Socket.io Connection Management
Server-Side (config/socket.ts):
typescript
import { Server } from 'socket.io';
import jwt from 'jsonwebtoken';
export const initializeSocket = (server: http.Server) => {
const io = new Server(server, {
cors: { origin: process.env.CLIENT_URL }
});
// Authentication middleware
io.use((socket, next) => {
const token = socket.handshake.auth.token;
if (!token) return next(new Error('Authentication required'));
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
socket.data.userId = decoded.userId;
next();
} catch (error) {
next(new Error('Invalid token'));
}
});
io.on('connection', (socket) => {
console.log(`User connected: ${socket.data.userId}`);
// Join user's personal room
socket.join(`user:${socket.data.userId}`);
// Subscribe to sport-specific channels
socket.on('subscribe:sport', ({ sport }) => {
socket.join(`sport:${sport}`);
});
socket.on('disconnect', () => {
console.log(`User disconnected: ${socket.data.userId}`);
});
});
return io;
};
// Emit functions
export const emitNewCode = (code: any) => {
io.to(`sport:${code.sport}`).emit('code:created', code);
};
export const emitCodePurchased = (codeId: string, sellerId: string) => {
io.to(`user:${sellerId}`).emit('code:purchased', { codeId });
};
export const emitWalletUpdate = (userId: string, balance: number) => {
io.to(`user:${userId}`).emit('wallet:updated', { balance });
};Security Architecture
Security Layers
┌─────────────────────────────────────┐
│ 1. Network Security │
│ (HTTPS, CORS, Rate Limiting) │
├─────────────────────────────────────┤
│ 2. Authentication │
│ (JWT, OTP, Refresh Tokens) │
├─────────────────────────────────────┤
│ 3. Authorization │
│ (RBAC, Resource Ownership) │
├─────────────────────────────────────┤
│ 4. Input Validation │
│ (Joi Schemas, Sanitization) │
├─────────────────────────────────────┤
│ 5. Data Protection │
│ (Hashing, Encryption, Hiding) │
└─────────────────────────────────────┘Security Features
- Password-less Authentication: Phone-based OTP eliminates password vulnerabilities
- JWT Tokens: Short-lived access tokens (15min), long-lived refresh tokens (7d)
- Token Rotation: Refresh tokens rotated on each use
- Bcrypt Hashing: OTP and refresh tokens hashed before storage
- Role-Based Access Control: Granular permissions (BUYER, SELLER, ADMIN)
- Input Validation: All inputs validated with Joi schemas
- SQL Injection Protection: MongoDB prevents SQL injection
- XSS Protection: Data sanitization and Content Security Policy
- CORS Configuration: Whitelisted origins only
- Rate Limiting: Prevents brute force and DDoS attacks
Scalability Considerations
Horizontal Scaling
mermaid
graph TB
A[Load Balancer] --> B[API Server 1]
A --> C[API Server 2]
A --> D[API Server 3]
B --> E[(MongoDB Primary)]
C --> E
D --> E
E --> F[(MongoDB Secondary)]
E --> G[(MongoDB Secondary)]
B --> H[(Redis Cache)]
C --> H
D --> H
style A fill:#FF9800
style E fill:#9C27B0
style H fill:#F44336Performance Optimizations
- Database Indexing: Strategic compound indexes for common queries
- Connection Pooling: Reuse MongoDB connections
- Caching Layer: Redis for frequently accessed data
- Pagination: All list endpoints paginated
- Lean Queries: Mongoose lean() for read-only operations
- Aggregation Pipeline: Efficient leaderboard calculations
- Lazy Loading: Code field hidden until purchase
- Virtual Fields: Calculated fields not stored in DB
Monitoring & Observability
Planned Implementation:
- Application Metrics: Response times, error rates, throughput
- Database Metrics: Query performance, connection pool usage
- Real-time Metrics: Socket.io connections, events per second
- Business Metrics: Transactions, user growth, revenue
- Logging: Structured logging with Winston
- Alerting: Critical error notifications via email/Slack
Deployment Architecture
Production Environment
┌─────────────────────────────────────────┐
│ Cloudflare (CDN + DDoS) │
└─────────────────┬───────────────────────┘
│
┌─────────────────▼───────────────────────┐
│ Nginx (Reverse Proxy) │
└─────────────────┬───────────────────────┘
│
┌───────────┴───────────┐
│ │
┌─────▼─────┐ ┌──────▼──────┐
│ API Server│ │ Socket.io │
│ (PM2) │ │ Server (PM2)│
└─────┬─────┘ └──────┬──────┘
│ │
└───────────┬──────────┘
│
┌───────────▼──────────┐
│ MongoDB Atlas │
│ (Replica Set) │
└──────────────────────┘Environment Variables
bash
# Server
NODE_ENV=production
PORT=6793
API_URL=https://api.oddxer.com
# Database
MONGODB_URI=mongodb+srv://...
DB_NAME=oddxer
# JWT
JWT_SECRET=your_secret_key
JWT_ACCESS_EXPIRY=15m
JWT_REFRESH_EXPIRY=7d
# OTP
OTP_EXPIRY=10m
OTP_LENGTH=6
# SMS Provider
SMS_API_KEY=...
SMS_SENDER_ID=ODDXER
# Mobile Money
MTN_API_KEY=...
AIRTEL_API_KEY=...
# Socket.io
SOCKET_CORS_ORIGIN=https://oddxer.com
# Redis (optional)
REDIS_URL=redis://...API Response Time Targets
| Endpoint Type | Target | Current |
|---|---|---|
| Authentication | <300ms | ~250ms |
| Code browsing | <200ms | ~180ms |
| Code purchase | <500ms | ~400ms |
| Wallet operations | <300ms | ~280ms |
| Leaderboard | <250ms | ~220ms |
| Voting | <200ms | ~150ms |
Future Architecture Enhancements
- Microservices: Split into Auth, Marketplace, Wallet, Voting services
- Message Queue: RabbitMQ/Kafka for async processing
- CDN: CloudFront for static asset delivery
- Redis Caching: Cache hot data (leaderboard, popular codes)
- Elasticsearch: Advanced search capabilities
- GraphQL: Alternative API for flexible queries
- gRPC: Inter-service communication
- Service Mesh: Istio for service management
- Kubernetes: Container orchestration
- Multi-Region: Deploy across multiple regions for low latency
Last Updated: 2025-12-01