PHP

Camada Controller: Recebendo Requisições e Orquestrando Respostas na Prática

7 min de leitura

Camada Controller: Recebendo Requisições e Orquestrando Respostas na Prática

O Papel do Controller na Arquitetura MVC 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. 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. Recebendo e Validando Requisições Captura de Dados 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

<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 &#039;express&#039;;

const app = express();

app.use(express.json());

interface CriarUsuarioDTO {

nome: string;

email: string;

idade: number;

}

app.post(&#039;/usuarios&#039;, (req: Request, res: Response) =&gt; {

// 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 &#039;class-validator&#039;;

import { validate } from &#039;class-validator&#039;;

class CriarUsuarioDTO {

@IsString()

@MinLength(3)

nome!: string;

@IsEmail()

email!: string;

@IsNumber()

idade!: number;

}

app.post(&#039;/usuarios&#039;, async (req: Request, res: Response) =&gt; {

const dto = Object.assign(new CriarUsuarioDTO(), req.body);

const erros = await validate(dto);

if (erros.length &gt; 0) {

return res.status(400).json({

erro: &#039;Validação falhou&#039;,

detalhes: erros.map(e =&gt; ({

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(&#039;Email já cadastrado&#039;);

}

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: &#039;Usuário criado com sucesso&#039;,

dados: usuario

});

} catch (erro) {

res.status(400).json({

erro: erro instanceof Error ? erro.message : &#039;Erro desconhecido&#039;

});

}

}

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: &#039;Usuário não encontrado&#039; });

}

res.json(usuario);

} catch (erro) {

res.status(500).json({ erro: &#039;Erro ao obter usuário&#039; });

}

}

}</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(&#039;Email já cadastrado&#039;)) {

return res.status(409).json({

codigo: &#039;CONFLITO&#039;,

mensagem: erro.message

});

}

if (erro.message.includes(&#039;não encontrado&#039;)) {

return res.status(404).json({

codigo: &#039;NAO_ENCONTRADO&#039;,

mensagem: erro.message

});

}

// Erro genérico

res.status(500).json({

codigo: &#039;ERRO_INTERNO&#039;,

mensagem: &#039;Ocorreu um erro ao processar a requisição&#039;

});

}

}

// Uso no controller

app.post(&#039;/usuarios&#039;, async (req: Request, res: Response) =&gt; {

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&lt;T&gt; {

sucesso: boolean;

dados?: T;

erro?: string;

timestamp: string;

}

class UsuarioController {

private formataResposta&lt;T&gt;(

dados: T | null,

erro?: string

): RespostaAPI&lt;T&gt; {

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,

&#039;Erro ao listar usuários&#039;

);

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, &#039;Falha na atualização&#039;);

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>

Comentários

Mais em PHP

Como Usar Deploy de Aplicações PHP em VPS com Nginx e PHP-FPM em Produção
Como Usar Deploy de Aplicações PHP em VPS com Nginx e PHP-FPM em Produção

Preparação da VPS e Instalação de Dependências Antes de deployar sua aplicaçã...

CRUD Completo com PDO: Create, Read, Update, Delete: Do Básico ao Avançado
CRUD Completo com PDO: Create, Read, Update, Delete: Do Básico ao Avançado

Preparando o Ambiente: Conexão com PDO A PDO (PHP Data Objects) é uma camada...

Como Usar Webhooks em PHP: Recebendo e Processando Eventos Externos em Produção
Como Usar Webhooks em PHP: Recebendo e Processando Eventos Externos em Produção

O que são Webhooks e Por Que Importam Webhooks são mecanismos de comunicação...