Separar responsabilidades
Uma aplicação de backend manutenível separa o código em camadas, cada uma com uma única responsabilidade. A requisição as atravessa de fora para dentro:
HTTP → Controlador → Serviço → Repositório → Banco de dados
Controlador (HTTP)
É o handler (req, res). Seu único trabalho é traduzir HTTP: ler
req.body/req.params, chamar o serviço e responder com o status e o
JSON adequados. Não contém regras de negócio nem SQL.
async function criar(req, res) {
const usuario = await servico.registrar(req.body);
res.status(201).json(usuario);
}
Serviço (lógica de negócio)
Contém as regras: validações de domínio, "não permitir emails
duplicados", calcular totais, orquestrar vários repositórios. Não sabe de
req/res nem de SQL; pede dados ao repositório.
async function registrar(dados) {
const existe = await repo.buscarPorEmail(dados.email);
if (existe) throw new Error("Email duplicado");
return repo.criar(dados);
}
Repositório (dados)
O que você viu antes: só acesso a dados.
Injeção de dependências
Repare que o serviço usa repo e o controlador usa servico. Em vez de
criar essas dependências dentro (const repo = new RepoSQL()), elas são recebidas
de fora. Isso é injeção de dependências:
function criarServico(repo) { // recebe o repo como parâmetro
return {
async registrar(dados) {
if (await repo.buscarPorEmail(dados.email)) {
throw new Error("Email duplicado");
}
return repo.criar(dados);
},
};
}
Vantagens: em produção você injeta o repositório real; nos testes você injeta um mock ou um repo em memória, sem tocar no serviço. Cada camada se testa isolada, e o conjunto fica desacoplado e fácil de mudar.