DIGITAL GAME STORE

Nuuvallace

Marketplace de jogos digitais com catálogo completo, carrinho funcional, filtros avançados por categoria e arquitetura frontend escalável — inspirado na experiência da Nuuvem.

Next.js TypeScript CSS Modules E-Commerce
Nuuvallace — Digital Game Store

O Objetivo

O Nuuvallace foi construído como um showcase de frontend e-commerce — um dos tipos de aplicação mais comuns em vagas front-end, mas que poucos candidatos demonstram domínio real. O projeto vai além de uma "lista de produtos": tem carrinho com persistência em localStorage, filtros multi-critério, buscas, e uma arquitetura de componentes pensada para escala.

A escolha da Nuuvem como inspiração foi intencional — é um produto real brasileiro que o público tech conhece, o que facilita a contextualização durante uma entrevista. "Você conhece a Nuuvem? Fiz um clone técnico dela."

Arquitetura & Decisões

UI Layer

  • • Next.js (App Router)
  • • TypeScript strict
  • • CSS Modules
  • • Componentes atômicos
  • • Responsivo mobile-first

State

  • • React Context API
  • • useReducer pattern
  • • localStorage persist
  • • Otimistic updates
  • • Cart count badge

Features

  • • Busca por nome
  • • Filtro por categoria
  • • Ordenação por preço
  • • Favoritos
  • • Detalhes do produto

CSS Modules — Por quê?

Escolhi CSS Modules ao invés de Tailwind para este projeto por uma razão específica: demonstrar que sei CSS de verdade. Qualquer dev usa Tailwind — mas escrever CSS escalável, sem conflitos de escopo, com naming semântico e variáveis CSS é uma habilidade rara. CSS Modules garante escopo local por padrão, sem poluir o global.

Context vs Redux

Para o carrinho, optei por Context API + useReducer ao invés de Redux. A justificativa é clara: a ferramenta certa para o escopo certo. Um marketplace com estado de carrinho não precisa do overhead do Redux. O padrão Reducer traz previsibilidade sem complexidade desnecessária — e demonstra que entendo quando usar cada abordagem.

context/CartContext.tsx
Cart State Management
type CartAction =
  | { type: 'ADD_ITEM'; payload: Game }
  | { type: 'REMOVE_ITEM'; payload: string }
  | { type: 'CLEAR_CART' };

function cartReducer(state: CartState, action: CartAction) {
  switch (action.type) {
    case 'ADD_ITEM':
      const exists = state.items.find(i => i.id === action.payload.id);
      if (exists) return state; // idempotente
      return { ...state, items: [...state.items, action.payload] };
    case 'REMOVE_ITEM':
      return { ...state, items: state.items.filter(i => i.id !== action.payload) };
    default: return state;
  }
}

Desafio: Filtros Combinados

Filtros por categoria + busca por nome + ordenação por preço precisavam ser compostos de forma declarativa, não imperativa. Um chain de ifs aninhados criaria código frágil. O desafio foi manter cada filtro isolado e combinável.

Solução: Pipeline Funcional

Implementei um pipeline de filtros via Array.reduce() — cada critério de filtro é uma função pura que recebe e retorna o array. Eles são compostos em sequência. Adicionar um novo filtro significa apenas adicionar uma função pura ao pipeline, sem tocar no código existente (Open/Closed Principle).

Aprendizados

CSS

CSS Modules força a pensar em escopo e naming desde o início. A experiência reforçou que CSS bem estruturado é tão importante quanto JS bem estruturado.

State

Context + Reducer é subestimado. Para muitos casos de uso, ele é suficiente e muito mais simples que Redux, que deve ser reservado para estados verdadeiramente complexos.

UX

Microtransições (hover nos cards, feedback do carrinho) fazem diferença desproporcional na percepção de qualidade do produto. Vale o investimento.