DEV Community

Cover image for Arquitetura em Camadas: Um guia para Organização de APIs
Leonardo Ferreira
Leonardo Ferreira

Posted on

Arquitetura em Camadas: Um guia para Organização de APIs

Entenda como a divisão entre Controller, Service e Repository garante escalabilidade e facilita a manutenção de sistemas modernos.

No desenvolvimento de software, a organização do código é tão importante quanto a sua funcionalidade. Uma API desestruturada pode funcionar no primeiro dia, mas rapidamente se torna um pesadelo de manutenção. Para evitar o acúmulo de dívida técnica, a indústria consolidou um padrão de divisão em três camadas principais: Controller, Service e Repository.

Esta estrutura baseia-se no princípio da Responsabilidade Única. Cada camada tem um papel estrito e não deve assumir tarefas que pertencem a outra.

1. Controller: A Interface de Entrada

A camada de Controller atua como o ponto de contato entre o cliente (como um aplicativo ou navegador) e a aplicação. Sua função é puramente administrativa e de fluxo.

  • Responsabilidades: Receber a requisição HTTP, validar a presença de parâmetros obrigatórios, extrair dados de cabeçalhos (headers) e do corpo (body) da mensagem.
  • O que não deve fazer: O Controller jamais deve conter regras de negócio, como cálculos de impostos ou validações de permissões complexas.

Após organizar os dados recebidos, o Controller os repassa para o Service correspondente. Assim que recebe o retorno, ele monta a resposta final com o status code adequado (200 OK, 201 Created, 400 Bad Request, etc.).

O que o Controller DEVE fazer:

  • Capturar dados: Ler parâmetros de URL, cabeçalhos (headers) e o corpo (body) da requisição.
  • Validar o formato: Verificar se o JSON enviado possui os campos obrigatórios e os tipos de dados corretos.
  • Delegar: Repassar os dados já organizados para o Service responsável.
  • Responder: Retornar o status HTTP adequado (ex: 201 para criação, 404 para não encontrado) e o corpo da resposta.

O que o Controller NÃO DEVE fazer:

  • Lógica de Negócio: Nunca deve realizar cálculos, aplicar descontos ou validar regras de permissão complexas.
  • Acesso a Dados: Jamais deve importar um modelo de banco de dados ou executar queries diretamente.

2. Service: O Coração da Lógica de Negócio

O Service é onde a inteligência da aplicação reside. É nesta camada que as regras de negócio são aplicadas, processamentos são realizados e cálculos são executados.

Uma característica fundamental do Service é que ele deve ser agnóstico à tecnologia de transporte. Isso significa que o Service não "sabe" o que é HTTP. Ele não deve lidar com objetos de requisição ou resposta do framework web.

Exemplo Prático: Se uma rota de busca permite filtrar nomes por vírgula (?nomes=Ana,João), o Controller deve transformar essa string em um array de strings antes de passá-la ao Service. O Service recebe o dado já estruturado, focado apenas na lógica de filtragem.

Além de desconhecer o protocolo de entrada, o Service também não interage diretamente com o banco de dados. Ele solicita os dados de que precisa à próxima camada: o Repository.

O que o Service DEVE fazer:

  • Processamento Central: Executar cálculos, aplicar regras de negócio e validações lógicas (ex: verificar se um saldo é suficiente).
  • Orquestração: Chamar múltiplos Repositories ou serviços externos se a operação exigir.
  • Trabalhar com Dados Estruturados: Receber tipos de dados limpos (como arrays e objetos) e não strings brutas vindas da URL.

O que o Service NÃO DEVE fazer:

  • Conhecer o HTTP: Não deve lidar com objetos de request ou response, nem saber o que é um "Status Code".
  • Montar Queries: Não deve conter código SQL ou sintaxe específica de bancos de dados. ## 3. Repository: A Camada de Persistência

O Repository é o especialista em dados. Sua única missão é gerenciar a comunicação com o mecanismo de armazenamento, seja ele um banco de dados SQL, NoSQL ou até uma integração com um sistema de arquivos.

  • Foco Técnico: Aqui ficam as queries (consultas) e a lógica de persistência.
  • Isolamento: O Repository não toma decisões de negócio. Ele apenas executa comandos como "buscar usuário por ID" ou "salvar nova transação".

Ao isolar o acesso aos dados, tornamos o sistema flexível. Se no futuro for necessário trocar o banco de dados, as alterações ficam restritas apenas aos Repositories, sem afetar a lógica central da aplicação.

O que o Repository DEVE fazer:

  • Operações de CRUD: Criar, ler, atualizar e deletar registros no banco de dados.
  • Abstrair a Query: Conter a implementação técnica da busca (ex: SELECT * FROM users WHERE id = ?).
  • Mapeamento: Converter os dados brutos do banco para objetos que o Service consiga entender.

O que o Repository NÃO DEVE fazer:

  • Tomar Decisões: Não deve decidir se um usuário "pode" ser deletado; ele apenas executa a deleção quando solicitado.
  • Encadear Lógicas: Evitar chamadas de um Repository para outro; essa coordenação deve vir do Service.

Por que utilizar esta estrutura?

A separação de contextos traz benefícios tangíveis para o ciclo de desenvolvimento:

  1. Testabilidade: Com responsabilidades divididas, podemos criar testes automatizados muito mais assertivos. É possível testar a lógica do Service isoladamente, simulando (mocking) as respostas do banco de dados.
  2. Reutilização de Código: Um método de um Repository, como o getUserById, pode ser utilizado por diversos Services diferentes. Isso evita a duplicidade de código e facilita correções globais.
  3. Manutenibilidade: Quando surge um erro, é mais fácil identificar onde ele está. Problemas de conexão com o banco? Verifique o Repository. Erro no cálculo de um desconto? O problema está no Service.

Top comments (0)