<h2>O Papel do Controller na Arquitetura MVC</h2>
<p>O controller é o intermediário fundamental entre a requisição do cliente e a lógica de negócio da aplicação. Ele recebe dados do usuário, valida entradas, coordena processos na camada de serviço e retorna respostas apropriadas. Em uma arquitetura bem estruturada, o controller não contém lógica complexa — apenas orquestra chamadas para serviços especializados. Sua responsabilidade primária é garantir que cada requisição HTTP seja processada corretamente e que a resposta seja formatada adequadamente para o cliente.</p>
<p>Entender o fluxo completo de uma requisição é essencial: cliente → rota → controller → serviço → repository → banco de dados → serviço → controller → resposta. O controller é o maestro que coordena este fluxo sem executar tarefas que não lhe cabem.</p>
<h2>Recebendo e Validando Requisições</h2>
<h3>Captura de Dados</h3>
<p>Os dados chegam ao controller através de diferentes fontes: URL (parâmetros), corpo da requisição (payload JSON) e headers. Cada linguagem e framework possui mecanismos específicos para extrair essas informações.</p>
<pre><code class="language-typescript">// Express.js com TypeScript
import express, { Request, Response } from 'express';
const app = express();
app.use(express.json());
interface CriarUsuarioDTO {
nome: string;
email: string;
idade: number;
}
app.post('/usuarios', (req: Request, res: Response) => {
// Captura do corpo da requisição
const { nome, email, idade }: CriarUsuarioDTO = req.body;
// Captura de parâmetros de URL
const id = req.params.id;
// Captura de query parameters
const filtro = req.query.filtro;
// Captura de headers
const token = req.headers.authorization;
res.json({ recebido: { nome, email, idade } });
});
app.listen(3000);</code></pre>
<h3>Validação de Entrada</h3>
<p>Validar dados no controller antes de enviar à camada de serviço é fundamental. A validação deve ser clara e retornar mensagens úteis ao cliente. Frameworks modernos oferecem bibliotecas dedicadas para esta tarefa.</p>
<pre><code class="language-typescript">// Usando classe-validator para validação
import { IsEmail, IsString, IsNumber, MinLength } from 'class-validator';
import { validate } from 'class-validator';
class CriarUsuarioDTO {
@IsString()
@MinLength(3)
nome!: string;
@IsEmail()
email!: string;
@IsNumber()
idade!: number;
}
app.post('/usuarios', async (req: Request, res: Response) => {
const dto = Object.assign(new CriarUsuarioDTO(), req.body);
const erros = await validate(dto);
if (erros.length > 0) {
return res.status(400).json({
erro: 'Validação falhou',
detalhes: erros.map(e => ({
campo: e.property,
mensagens: Object.values(e.constraints || {})
}))
});
}
// Prosseguir com a lógica
res.status(201).json({ id: 1, ...dto });
});</code></pre>
<h2>Orquestrando a Lógica de Negócio</h2>
<h3>Coordenação entre Camadas</h3>
<p>O controller chama serviços, que por sua vez acessam repositories. O controller nunca deve executar queries diretas ou operações de banco de dados. Sua função é coordenar e garantir que erros sejam tratados apropriadamente.</p>
<pre><code class="language-typescript">// Serviço de usuário (camada de negócio)
class UsuarioService {
constructor(private usuarioRepository: UsuarioRepository) {}
async criarUsuario(dados: CriarUsuarioDTO) {
const usuarioExistente = await this.usuarioRepository
.findByEmail(dados.email);
if (usuarioExistente) {
throw new Error('Email já cadastrado');
}
const usuarioCriado = await this.usuarioRepository
.save(dados);
return usuarioCriado;
}
async obterUsuarioPorId(id: number) {
return this.usuarioRepository.findById(id);
}
}
// Controller orquestrando o serviço
class UsuarioController {
constructor(private usuarioService: UsuarioService) {}
async criar(req: Request, res: Response) {
try {
const dados = req.body;
const usuario = await this.usuarioService.criarUsuario(dados);
res.status(201).json({
mensagem: 'Usuário criado com sucesso',
dados: usuario
});
} catch (erro) {
res.status(400).json({
erro: erro instanceof Error ? erro.message : 'Erro desconhecido'
});
}
}
async obter(req: Request, res: Response) {
try {
const id = parseInt(req.params.id);
const usuario = await this.usuarioService.obterUsuarioPorId(id);
if (!usuario) {
return res.status(404).json({ erro: 'Usuário não encontrado' });
}
res.json(usuario);
} catch (erro) {
res.status(500).json({ erro: 'Erro ao obter usuário' });
}
}
}</code></pre>
<h3>Tratamento de Erros e Exceções</h3>
<p>Erros devem ser capturados e convertidos em respostas HTTP apropriadas. Código de status, mensagens claras e estrutura consistente melhoram a experiência da API.</p>
<pre><code class="language-typescript">// Middleware de tratamento global de erros
class ErrorHandler {
static handle(erro: Error, res: Response) {
if (erro.message.includes('Email já cadastrado')) {
return res.status(409).json({
codigo: 'CONFLITO',
mensagem: erro.message
});
}
if (erro.message.includes('não encontrado')) {
return res.status(404).json({
codigo: 'NAO_ENCONTRADO',
mensagem: erro.message
});
}
// Erro genérico
res.status(500).json({
codigo: 'ERRO_INTERNO',
mensagem: 'Ocorreu um erro ao processar a requisição'
});
}
}
// Uso no controller
app.post('/usuarios', async (req: Request, res: Response) => {
try {
const usuario = await usuarioService.criarUsuario(req.body);
res.status(201).json(usuario);
} catch (erro) {
ErrorHandler.handle(erro as Error, res);
}
});</code></pre>
<h2>Formatando e Retornando Respostas</h2>
<p>O controller é responsável pela estrutura final da resposta. Códigos HTTP corretos, headers apropriados e formato consistente são essenciais para uma API profissional. A resposta deve sempre considerar o contexto da operação: sucesso, validação ou erro.</p>
<pre><code class="language-typescript">// Resposta estruturada com padrão consistente
interface RespostaAPI<T> {
sucesso: boolean;
dados?: T;
erro?: string;
timestamp: string;
}
class UsuarioController {
private formataResposta<T>(
dados: T | null,
erro?: string
): RespostaAPI<T> {
return {
sucesso: !erro,
dados: dados ?? undefined,
erro,
timestamp: new Date().toISOString()
};
}
async listar(req: Request, res: Response) {
try {
const usuarios = await this.usuarioService.listarTodos();
const resposta = this.formataResposta(usuarios);
res.json(resposta);
} catch (erro) {
const resposta = this.formataResposta(
null,
'Erro ao listar usuários'
);
res.status(500).json(resposta);
}
}
async atualizar(req: Request, res: Response) {
try {
const id = parseInt(req.params.id);
const usuarioAtualizado = await this.usuarioService
.atualizar(id, req.body);
const resposta = this.formataResposta(usuarioAtualizado);
res.json(resposta);
} catch (erro) {
const resposta = this.formataResposta(null, 'Falha na atualização');
res.status(400).json(resposta);
}
}
}</code></pre>
<h2>Conclusão</h2>
<p>Os pontos principais para dominar a camada controller são: <strong>(1)</strong> receba dados de múltiplas fontes e valide-os rigorosamente antes de processar — erros detectados cedo economizam tempo e recursos; <strong>(2)</strong> orquestre a comunicação entre requisição e serviços, nunca executando lógica complexa diretamente no controller — ele deve ser um maestro, não um músico; <strong>(3)</strong> formate respostas consistentemente com códigos HTTP apropriados e estruturas padronizadas — a qualidade da resposta reflete a qualidade da sua API.</p>
<p>Um controller bem escrito é enxuto, testável e fácil de manter. Investir tempo estruturando-o adequadamente economiza horas de debugging posterior.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://expressjs.com/en/guide/routing.html" target="_blank" rel="noopener noreferrer">Express.js Documentation - Routing</a></li>
<li><a href="https://typeorm.io/" target="_blank" rel="noopener noreferrer">TypeORM - Controllers Best Practices</a></li>
<li><a href="https://github.com/typestack/class-validator" target="_blank" rel="noopener noreferrer">class-validator - Validation Library</a></li>
<li><a href="https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design" target="_blank" rel="noopener noreferrer">RESTful API Design Guidelines - Microsoft</a></li>
<li><a href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html" target="_blank" rel="noopener noreferrer">Clean Architecture - Robert C. Martin</a></li>
</ul>