Skill de Desenvolvimento Full-Stack (UI & Backend)
Esta skill fornece um guia arquitetônico de ponta a ponta, um runbook de fluxo de trabalho e padrões de codificação para o desenvolvimento de aplicações web de nível de produção com TypeScript/JavaScript moderno, tanto no frontend (UI) quanto no backend.
1. Princípios Arquitetônicos Principais
- Segurança de Tipos de Ponta a Ponta:
- Fonte única de verdade para modelos de domínio e objetos de transferência de dados (DTOs).
- Use bibliotecas de validação de esquemas (como Zod) para analisar e validar requisições em tempo de execução e inferir tipos TypeScript em tempo de compilação.
- Separação em Camadas de Responsabilidades:
- Frontend: Separação estrita entre primitivas de apresentação, componentes de negócio/contêiner e hooks de busca de dados.
- Backend: Separação estrita entre Roteamento/Controladores (transporte HTTP), Camada de Serviço (lógica de negócio) e Camada de Acesso a Dados / Repositório (banco de dados / ORM).
- Contrato de API Primeiro:
- Estabeleça contratos de requisição e resposta antes de implementar detalhes de UI ou backend.
- Envelope de resposta uniforme:
{ success: true, data: T }ou{ success: false, error: { message, code, details } }.
- Resiliência e Tratamento de Erros:
- Frontend: Limites de erro, estados de carregamento esqueleto, fallbacks elegantes e notificações amigáveis ao usuário.
- Backend: Middleware de erro centralizado, categorização de erros operacionais vs. de programador, logging estruturado e padrões seguros por padrão.
- Segurança por Padrão:
- Headers de segurança (Helmet), políticas CORS explícitas, validação/sanitização de entrada, limitação de taxa e autenticação segura (cookies HTTP-only ou tokens bearer).
2. Estrutura de Diretórios Recomendada
Para repositórios full-stack (monorepo ou estrutura dividida):
├── packages/ or apps/
│ ├── client/ # Frontend UI (React / Next.js / Vite)
│ │ ├── src/
│ │ │ ├── assets/ # Ativos estáticos (imagens, ícones, fontes)
│ │ │ ├── components/ # Componentes de UI (átomos, moléculas, organismos)
│ │ │ │ ├── ui/ # Primitivas do sistema de design principal (Button, Input, Modal)
│ │ │ │ └── features/ # Componentes compostos específicos de domínio
│ │ │ ├── hooks/ # Hooks React customizados (useAuth, useDebounce, etc.)
│ │ │ ├── services/ # Cliente de API e integrações de serviço
│ │ │ ├── store/ # Gerenciamento de estado global (Zustand, Context, etc.)
│ │ │ ├── types/ # Tipos específicos do cliente
│ │ │ ├── utils/ # Funções auxiliares e utilitários de formatação
│ │ │ ├── App.tsx # Componente principal da aplicação e rotas
│ │ │ ├── main.tsx # Ponto de entrada
│ │ │ └── index.css # CSS global e tokens de design
│ │ └── package.json
│ │
│ ├── server/ # Backend API (Node.js / Express / Fastify)
│ │ ├── src/
│ │ │ ├── config/ # Variáveis de ambiente e configurações em tempo de execução
│ │ │ ├── controllers/ # Manipuladores de rota HTTP (extrair parâmetros, chamar serviços)
│ │ │ ├── middleware/ # Autenticação, validação, manipulador de erro, limitador de taxa
│ │ │ ├── models/ # Modelos/esquemas de banco de dados (Prisma, Drizzle, etc.)
│ │ │ ├── routes/ # Definições de rota Express/Fastify
│ │ │ ├── services/ # Lógica de negócio e interações com API externa
│ │ │ ├── utils/ # Auxiliares do backend (jwt, logger, crypto)
│ │ │ ├── app.ts # Configuração da aplicação Express/Fastify
│ │ │ └── server.ts # Ponto de entrada do servidor e desligamento gracioso
│ │ └── package.json
│ │
│ └── shared/ # Esquemas compartilhados, DTOs e contratos TypeScript
│ ├── src/
│ │ ├── schemas/ # Esquemas Zod (user.schema.ts, auth.schema.ts)
│ │ └── types/ # Tipos inferidos (export type User = z.infer<...>)
│ └── package.json
3. Fluxo de Trabalho de Desenvolvimento Passo a Passo
Ao implementar qualquer funcionalidade full-stack, execute as seguintes fases em ordem:
Fase 1: Definir Contratos e Esquemas Compartilhados
- Defina o esquema Zod representando os payloads de requisição e entidades de resposta em
shared/oucommon/. - Exporte os tipos TypeScript inferidos.
import { z } from 'zod';
export const CreateUserSchema = z.object({
email: z.string().email('Endereço de email inválido'),
name: z.string().min(2, 'O nome deve ter pelo menos 2 caracteres'),
role: z.enum(['admin', 'user']).default('user'),
});
export type CreateUserInput = z.infer<typeof CreateUserSchema>;
export type UserResponse = CreateUserInput & { id: string; createdAt: string };
Fase 2: Construir o Serviço Backend e Endpoints
- Middleware de Validação: Aplique middleware de validação com o esquema Zod para rejeitar entradas malformadas cedo com HTTP 400.
- Camada de Serviço: Escreva lógica de negócio pura em uma classe ou módulo de serviço. Isole consultas de banco de dados de conceitos HTTP (
req,res). - Controlador: Manipule a extração de entrada HTTP, chame o serviço e retorne respostas padronizadas usando
sendSuccess(). - Tratamento de Erros: Use erros personalizados (
NotFoundError,BadRequestError,UnauthorizedError) e deixe o middleware de erro centralizado capturar e formatar a resposta.
Fase 3: Construir os Componentes de UI do Frontend
- Tokens do Sistema de Design: Garanta que as propriedades personalizadas CSS ou variáveis do sistema de design existam para cores, tipografia, espaçamento e sombras.
- Primitivas Atômicas / UI: Construa ou reutilize componentes base de UI (botões, campos de entrada, badges, diálogos) com navegação por teclado e atributos ARIA.
- Visão / Página de Recurso: Monte componentes compostos com hierarquia visual limpa, espaçamento generoso, tipografia moderna e layout responsivo com grid/flexbox.
- Estado e Micro-interações:
- Forneça feedback visual para estados de hover, active, focus-visible e disabled.
- Inclua esqueletos de carregamento, indicadores de spinner e botões desabilitados durante submissões pendentes.
Fase 4: Integração e Busca de Dados
- Cliente de API: Use um wrapper de fetch centralizado ou instância Axios configurada com URL base, timeout, credenciais e interceptadores.
- Estado do Servidor: Use TanStack Query (React Query) ou hooks padrão para consultar e modificar dados.
- Invalidação de cache em mutações bem-sucedidas.
- Atualizações otimistas quando apropriado para uma experiência de usuário rápida.
- Manipulação de Formulários: Integre formulários com React Hook Form + resolvedor Zod para compartilhar exatamente as mesmas regras de validação entre cliente e servidor.
Fase 5: Verificação e Hardening
- Verificação do Backend: Teste endpoints com testes de unidade/integração ou curl/fetch para verificar casos de borda (entradas inválidas, tokens ausentes, chaves duplicadas).
- Verificação do Frontend: Verifique a responsividade em larguras de viewport de desktop, tablet e celular.
- Verificação de Segurança: Confirme que CORS, limitação de taxa e guards de autenticação estão ativos.
4. Padrões de Código Rápidos
Auxiliar de Resposta de API Padrão (Backend)
// server/src/utils/response.ts
import { Response } from 'express';
export function sendSuccess<T>(res: Response, data: T, statusCode = 200, meta?: unknown) {
return res.status(statusCode).json({
success: true,
data,
...(meta ? { meta } : {}),
});
}
export function sendError(res: Response, message: string, statusCode = 500, details?: unknown) {
return res.status(statusCode).json({
success: false,
error: {
message,
statusCode,
details: details ?? null,
},
});
}
Middleware de Erro Centralizado Express
// server/src/middleware/errorHandler.ts
import { Request, Response, NextFunction } from 'express';
import { ZodError } from 'zod';
export class AppError extends Error {
constructor(public message: string, public statusCode = 500, public details?: unknown) {
super(message);
this.name = 'AppError';
}
}
export function errorHandler(err: unknown, req: Request, res: Response, next: NextFunction) {
if (err instanceof ZodError) {
return res.status(400).json({
success: false,
error: {
message: 'Falha na validação',
statusCode: 400,
details: err.flatten().fieldErrors,
},
});
}
if (err instanceof AppError) {
return res.status(err.statusCode).json({
success: false,
error: {
message: err.message,
statusCode: err.statusCode,
details: err.details ?? null,
},
});
}
console.error('Erro de Servidor Não Tratado:', err);
return res.status(500).json({
success: false,
error: {
message: 'Erro interno do servidor',
statusCode: 500,
},
});
}
Cliente de API com Segurança de Tipos (Frontend)
// client/src/services/apiClient.ts
interface ApiResponse<T> {
success: boolean;
data: T;
error?: {
message: string;
statusCode: number;
details?: unknown;
};
}
export async function apiRequest<T>(url: string, options?: RequestInit): Promise<T> {
const res = await fetch(url, {
headers: {
'Content-Type': 'application/json',
...options?.headers,
},
...options,
});
const body: ApiResponse<T> = await res.json();
if (!res.ok || !body.success) {
throw new Error(body.error?.message || `HTTP ${res.status}: Falha na requisição`);
}
return body.data;
}
5. Referências e Aprofundamentos
Para padrões arquitetônicos aprofundados, consulte: