Stack Tecnológica
Esta página detalha todas as tecnologias utilizadas no CSGOFlip e justifica cada escolha comparando com alternativas.
Visão Geral
| Camada | Tecnologia | Versão |
|---|---|---|
| Runtime | Node.js | 18+ |
| Linguagem | TypeScript | 5.3+ |
| Framework Backend | NestJS | 10+ |
| HTTP Adapter | Fastify | 4+ |
| Banco de Dados | PostgreSQL | 14+ |
| ORM | Prisma | 5+ |
| Cache/Pub-Sub | Redis | 7+ |
| Filas | BullMQ | 4+ |
| WebSocket | Socket.io | 4+ |
| Frontend | Next.js | 14+ |
| Admin | Next.js + shadcn/ui | 16+ |
| Styling | Tailwind CSS | 3.4+ |
| Animações | Framer Motion | 11+ |
Backend
NestJS vs Express Puro
| Aspecto | NestJS | Express Puro |
|---|---|---|
| Estrutura | ✅ Opinativo, organizado | ❌ Livre demais, vira bagunça |
| DI (Dependency Injection) | ✅ Nativo, poderoso | ❌ Precisa de lib externa |
| TypeScript | ✅ First-class support | ⚠️ Funciona, mas não é ideal |
| Decorators | ✅ @Controller, @Get, etc | ❌ Não tem |
| Módulos | ✅ Sistema de módulos | ❌ Não tem conceito |
| Validação | ✅ class-validator integrado | ❌ Precisa configurar |
| Documentação | ✅ Swagger automático | ❌ Manual |
| Guards/Pipes | ✅ Nativos | ❌ Middleware genérico |
| Testing | ✅ Jest integrado | ⚠️ Configuração manual |
| WebSocket | ✅ Gateway nativo | ❌ Precisa de lib |
| Microservices | ✅ Suporte nativo | ❌ Não tem |
Por que NestJS?
Para um sistema de gambling com 17 módulos e 95+ use cases, precisamos de organização forçada. Express deixaria o código virar espaguete. NestJS nos dá arquitetura enterprise-grade com DI, módulos e decorators.
Fastify vs Express Adapter
| Aspecto | Fastify | Express |
|---|---|---|
| Performance | ✅ ~2x mais rápido | ❌ Mais lento |
| JSON Parsing | ✅ Otimizado | ❌ body-parser necessário |
| Schema Validation | ✅ JSON Schema nativo | ❌ Precisa de lib |
| Logging | ✅ Pino integrado | ❌ Morgan/Winston manual |
| Hooks | ✅ Lifecycle hooks | ⚠️ Middleware chain |
| TypeScript | ✅ Tipos incluídos | ⚠️ @types necessário |
Por que Fastify?
Em um sistema de gambling com milhares de requisições por segundo, performance importa. Fastify é ~2x mais rápido que Express, especialmente em JSON parsing.
// main.ts - Configuração do Fastify
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter({ logger: true }),
);Banco de Dados
PostgreSQL vs MongoDB
| Aspecto | PostgreSQL | MongoDB |
|---|---|---|
| Transações ACID | ✅ Completo | ⚠️ Limitado |
| Relacionamentos | ✅ Foreign Keys | ❌ Referências manuais |
| Consistência | ✅ Strong consistency | ⚠️ Eventual consistency |
| Queries Complexas | ✅ SQL poderoso | ⚠️ Aggregation pipeline |
| Integridade | ✅ Constraints nativos | ❌ Validação na app |
| Auditoria | ✅ Triggers, extensions | ⚠️ Change streams |
| Particionamento | ✅ Nativo | ✅ Sharding |
| Full-Text Search | ✅ Nativo | ✅ Atlas Search |
Por que PostgreSQL?
Sistema financeiro EXIGE transações ACID e integridade referencial. MongoDB não garante consistência em operações financeiras complexas. Double-entry bookkeeping precisa de constraints de banco.
-- Exemplo: Constraint que garante integridade financeira
ALTER TABLE transactions
ADD CONSTRAINT check_transaction_integrity
CHECK (
(type = 'DEBIT' AND amount_cents < 0) OR
(type = 'CREDIT' AND amount_cents > 0)
);Prisma vs TypeORM
| Aspecto | Prisma | TypeORM |
|---|---|---|
| Type Safety | ✅ 100% type-safe | ⚠️ Decorators podem falhar |
| Schema | ✅ prisma.schema declarativo | ❌ Decorators no código |
| Migrations | ✅ Automáticas | ⚠️ Manuais ou sync |
| Performance | ✅ Query engine otimizado | ⚠️ ORM overhead |
| Relations | ✅ Fácil de definir | ⚠️ Decorators verbosos |
| Raw Queries | ✅ $queryRaw | ✅ query() |
| Studio | ✅ Prisma Studio | ❌ Não tem |
Por que Prisma?
Type-safety absoluto. Quando você muda o schema, o TypeScript quebra em todos os lugares que precisam ser atualizados. TypeORM usa decorators que podem ter tipos errados silenciosamente.
// schema.prisma - Declarativo e limpo
model User {
id BigInt @id @default(autoincrement())
steamId String @unique
username String
balanceCents BigInt @default(0)
transactions Transaction[]
inventory UserInventory[]
battles BattlePlayer[]
}Cache e Mensageria
Redis vs Memcached
| Aspecto | Redis | Memcached |
|---|---|---|
| Estruturas | ✅ Strings, Lists, Sets, Hashes, Sorted Sets | ❌ Apenas strings |
| Pub/Sub | ✅ Nativo | ❌ Não tem |
| Persistência | ✅ RDB/AOF | ❌ Apenas memória |
| Lua Scripts | ✅ Suporta | ❌ Não tem |
| Cluster | ✅ Redis Cluster | ⚠️ Consistent hashing |
| Distributed Locks | ✅ Redlock | ❌ Não tem |
Por que Redis?
Precisamos de Pub/Sub para WebSocket scaling, Distributed Locks para race conditions financeiras, e estruturas de dados complexas (sorted sets para leaderboards). Memcached é simples demais.
// Uso de Redis para múltiplos propósitos
// 1. Sessions
await redis.set(`session:${sessionId}`, JSON.stringify(session), 'EX', 604800);
// 2. Cache
await redis.set(`case:${caseId}`, JSON.stringify(caseData), 'EX', 3600);
// 3. Distributed Lock
const lock = await redlock.acquire([`lock:user:${userId}`], 5000);
// 4. Pub/Sub para WebSocket
redis.publish('balance:update', JSON.stringify({ userId, balance }));BullMQ vs Bull vs Agenda
| Aspecto | BullMQ | Bull | Agenda |
|---|---|---|---|
| TypeScript | ✅ Nativo | ⚠️ @types | ❌ Fraco |
| Performance | ✅ Melhor | ⚠️ Bom | ❌ MongoDB overhead |
| Retry | ✅ Avançado | ✅ Bom | ⚠️ Básico |
| Rate Limiting | ✅ Nativo | ⚠️ Plugin | ❌ Manual |
| Concurrency | ✅ Por worker | ✅ Por worker | ⚠️ Limitado |
| Dashboard | ✅ Bull Board | ✅ Arena | ❌ Não tem |
Por que BullMQ?
É a evolução do Bull com melhor suporte a TypeScript e performance. Agenda usa MongoDB, que não queremos adicionar ao stack.
WebSocket
Socket.io vs WS Puro vs Pusher
| Aspecto | Socket.io | WS Puro | Pusher |
|---|---|---|---|
| Fallback | ✅ Polling automático | ❌ Apenas WS | ✅ Gerenciado |
| Rooms | ✅ Nativo | ❌ Manual | ✅ Channels |
| Reconnect | ✅ Automático | ❌ Manual | ✅ Gerenciado |
| Scaling | ✅ Redis Adapter | ❌ Sticky sessions | ✅ Gerenciado |
| Custo | ✅ Gratuito | ✅ Gratuito | ❌ Pago |
| Controle | ✅ Total | ✅ Total | ❌ Limitado |
Por que Socket.io?
Rooms nativas para batalhas e usuários, reconnect automático para mobile, Redis Adapter para escalar horizontalmente. WS puro precisaria de muito código extra.
// WebSocket Gateway com Socket.io
@WebSocketGateway({ cors: true })
export class WebSocketGateway {
@WebSocketServer()
server: Server;
// Sala por usuário (privada)
handleConnection(client: Socket) {
client.join(`user:${userId}`);
}
// Broadcast para sala de batalha
emitBattleUpdate(battleId: bigint, data: any) {
this.server.to(`battle:${battleId}`).emit('battle:update', data);
}
// Broadcast global (live drops)
emitLiveDrop(drop: LiveDropDto) {
this.server.emit('live:drop', drop);
}
}Frontend
Next.js vs React + Vite vs Remix
| Aspecto | Next.js | React + Vite | Remix |
|---|---|---|---|
| SSR | ✅ Nativo | ❌ Manual | ✅ Nativo |
| SSG | ✅ Nativo | ❌ Manual | ⚠️ Limitado |
| Routing | ✅ File-based | ❌ React Router | ✅ File-based |
| API Routes | ✅ Nativo | ❌ Não tem | ✅ Loaders |
| Image Optimization | ✅ next/image | ❌ Manual | ❌ Manual |
| Ecosystem | ✅ Maior | ✅ Grande | ⚠️ Crescendo |
| Vercel Deploy | ✅ 1 click | ⚠️ Configuração | ⚠️ Configuração |
Por que Next.js?
App Router do Next.js 14 é perfeito para nossa arquitetura. Server Components para páginas estáticas (caixas), Client Components para interação (roleta). Deploy no Vercel em 1 click.
Tailwind CSS vs CSS Modules vs Styled Components
| Aspecto | Tailwind | CSS Modules | Styled Components |
|---|---|---|---|
| Bundle Size | ✅ Purged (pequeno) | ✅ Pequeno | ❌ Runtime JS |
| DX | ✅ Muito rápido | ⚠️ Arquivos extras | ✅ Colocalizado |
| Consistência | ✅ Design system forçado | ❌ Livre | ❌ Livre |
| Responsivo | ✅ Classes (sm:, md:) | ❌ Media queries | ❌ Media queries |
| Dark Mode | ✅ dark: prefix | ❌ Manual | ❌ ThemeProvider |
| Animações | ✅ animate-* | ❌ Manual | ⚠️ Keyframes |
Por que Tailwind?
Velocidade de desenvolvimento incomparável. Responsivo, dark mode, hover states - tudo com classes. Combinado com Framer Motion para animações complexas da roleta.
Autenticação
Sessions (Redis) vs JWT vs OAuth Tokens
| Aspecto | Sessions | JWT | OAuth Tokens |
|---|---|---|---|
| Revogação | ✅ Instantânea | ❌ Blacklist necessária | ⚠️ Refresh token |
| Tamanho | ✅ ID pequeno | ❌ Payload grande | ⚠️ Médio |
| Stateless | ❌ Precisa de storage | ✅ Stateless | ⚠️ Refresh precisa |
| Multi-device | ✅ Lista de sessões | ❌ Difícil controlar | ⚠️ Complexo |
| Logout All | ✅ Simples | ❌ Muito difícil | ⚠️ Complexo |
Por que Sessions?
Em gambling, precisamos poder banir usuários instantaneamente. Com JWT, o token continua válido até expirar. Com sessions Redis, deletamos a sessão e acabou.
// Banir usuário = deletar todas as sessões
async banUser(userId: bigint) {
// 1. Marca no banco
await this.userRepo.update(userId, { status: 'BANNED' });
// 2. Remove TODAS as sessões (logout instantâneo)
const sessionKeys = await this.redis.keys(`session:*:${userId}`);
await this.redis.del(...sessionKeys);
// Usuário é desconectado imediatamente, não importa
// quantos dispositivos esteja usando
}IDs
Snowflake vs UUID vs Auto-increment
| Aspecto | Snowflake | UUID | Auto-increment |
|---|---|---|---|
| Tamanho | ✅ 8 bytes (BigInt) | ❌ 16 bytes | ✅ 4-8 bytes |
| Ordenação | ✅ Por tempo | ❌ Aleatório | ✅ Sequencial |
| Distribuído | ✅ Sem coordenação | ✅ Sem coordenação | ❌ Precisa de sequência |
| Index Performance | ✅ Excelente | ❌ Fragmentação | ✅ Excelente |
| Previsibilidade | ⚠️ Timestamp visível | ✅ Completamente aleatório | ❌ Sequencial |
Por que Snowflake?
Performance de índice igual a auto-increment, geração distribuída sem coordenação central, e ordenação natural por timestamp. UUID fragmenta índices B-tree.
// Estrutura do Snowflake ID (64 bits)
// | Timestamp (41 bits) | Worker ID (10 bits) | Sequence (12 bits) |
class SnowflakeService {
private sequence = 0n;
private lastTimestamp = -1n;
generate(): bigint {
let timestamp = BigInt(Date.now());
if (timestamp === this.lastTimestamp) {
this.sequence = (this.sequence + 1n) & 0xFFFn; // 12 bits
if (this.sequence === 0n) {
timestamp = this.waitNextMillis(timestamp);
}
} else {
this.sequence = 0n;
}
this.lastTimestamp = timestamp;
return ((timestamp - EPOCH) << 22n) |
(this.workerId << 12n) |
this.sequence;
}
}Valores Monetários
BigInt (centavos) vs Decimal vs Float
| Aspecto | BigInt (centavos) | Decimal | Float |
|---|---|---|---|
| Precisão | ✅ Exata | ✅ Exata | ❌ Impreciso |
| Operações atômicas | ✅ Simples | ⚠️ Biblioteca | ❌ Problemático |
| Performance | ✅ Nativo | ⚠️ Overhead | ✅ Nativo |
| Comparação | ✅ Direta | ⚠️ Método | ❌ Problemático |
| Armazenamento | ✅ BIGINT | ✅ DECIMAL | ❌ Não recomendado |
NUNCA use Float para dinheiro!
0.1 + 0.2 === 0.3 // false! Resultado: 0.30000000000000004Por que BigInt em centavos?
Zero imprecisão. R$ 10,50 = 1050 centavos. Operações são simples somas/subtrações de inteiros. Não precisa de biblioteca de precisão arbitrária.
// Conversões
function toCents(reais: number): bigint {
return BigInt(Math.round(reais * 100));
}
function toReais(cents: bigint): number {
return Number(cents) / 100;
}
// Uso
const price = 1050n; // R$ 10,50
const balance = 5000n; // R$ 50,00
const remaining = balance - price; // 3950n = R$ 39,50Resumo das Escolhas
| Decisão | Escolha | Motivo Principal |
|---|---|---|
| Framework | NestJS | Organização forçada, DI nativo |
| HTTP | Fastify | 2x mais rápido |
| Banco | PostgreSQL | ACID para financeiro |
| ORM | Prisma | Type-safety absoluto |
| Cache | Redis | Estruturas + Pub/Sub + Locks |
| Filas | BullMQ | TypeScript + Performance |
| WebSocket | Socket.io | Rooms + Reconnect + Adapter |
| Frontend | Next.js 14 | SSR + App Router |
| CSS | Tailwind | Velocidade de dev |
| Auth | Sessions | Revogação instantânea |
| IDs | Snowflake | Performance + Distribuído |
| Dinheiro | BigInt (cents) | Precisão absoluta |
Cada escolha foi feita pensando em segurança, performance e manutenibilidade para um sistema de gambling em produção.
