Já falamos sobre APIs por aqui, e temos até um artigo recomendando boas práticas de desenvolvimento de uma aplicação RESTful, mas uma dúvida sempre constante é como construir nossas aplicações. Qual arquitetura utilizo? Como posso montar a estrutura do código? Como organizar meus arquivos? No texto de hoje, vou apresentar uma estrutura simples, porém bem eficiente para você construir a sua API.
Tento aqui construir uma aplicação sem muita complexidade ou impeditivos, realmente da forma mais simples. Temos algumas alternativas como o Clean Architecture, que vem se tornando bastante popular, mas a ideia é mesmo ser bastante direto, sem nos esquecer de conceitos importantes de desenvolvimento e boas práticas de programação.
O que não pode faltar em uma API?
Precisamos definir nossas rotas; para gerenciar essas rotas, precisaremos dos controllers; e por fim, para trazer funcionalidade a nossa aplicação, precisaremos dos services.
Este é um modelo bastante simples, padrão até mesmo do NestJS, Laravel e outros frameworks de API. É a partir deste modelo que vamos nos aprofundar um pouco, explorar recursos e tentar definir uma API simples e eficiente.
Montando a estrutura
Veja bem que por aqui, definirei muito bem a responsabilidade de cada estrutura necessária para a API. É importante que siga muito bem a sua função, não fazendo nada mais, além disso. Gosto também de organizar os arquivos de acordo com essa estrutura, cada qual em sua pasta, cada arquivo de acordo com a sua funcionalidade —por ex: na pasta rotas, os arquivos products.route.ts
e payment.route.ts
Rotas
Toda API começa pela definição de rotas: os caminhos, ou endpoints, que nossas aplicações clientes irão utilizar para solicitar e receber informações da nossa API.
As rotas devem ser sempre bem escritas, de forma clara e intuitiva a quem vá utilizar. É bastante importante seguir bem os padrões de servidores HTTP, que irão até te ajudar bastante na legibilidade de rotas.
As rotas devem ser responsáveis por definir os caminhos, e enviar aos controllers. Apenas isso.
Se você usa o NestJS será bem interessante, pois rotas e controllers trabalham juntos, como explicamos em nosso texto sobre o NestJS.
Controllers
Os controllers são responsáveis por gerenciar as respostas exibidas ao cliente em uma API. Cliente solicitou a informação de um produto? Exibir o produto. O produto não existe? Retornar um erro 404, com uma mensagem de que este produto não está disponível.
Gosto de limitar os controllers somente a isso. Não é função de um controller procurar um produto em um banco de dados, alterar uma informação, ou qualquer coisa do tipo. O controller é responsável somente por tratar as respostas. Dessa forma, conseguimos deixar nosso código bastante organizado.
Outra função do controller, é de processar as funcionalidades recebidas de o “service” —este, sim, responsável por trazer funcionalidade a API.
Services
Um service tem como responsabilidade centralizar todas as funcionalidades de uma API. Assim como no exemplo que dei de controller: quer buscar um produto? É uma função no service que será responsável por isso. Quer adicionar um novo registro no seu banco de dados? É novamente uma função no service que será responsável por isso.
Em uma aplicação, podemos ter vários services. É importante que eles sejam divididos conforme a funcionalidade: um service para funções dos produtos, um service para funções de pagamento, um service para funções de compra de produtos, e por aí seguimos. Não é um problema, muito pelo contrário, ter uma variedade de services como no exemplo que dei, é justamente isso que ajudará a manter o seu código bem organizado.
Mas claro que, nem tudo precisa estar em um service —é importante apenas que o service centralize as funções. Por exemplo, para cuidar de ações diretamente no banco de dados, podemos ter “repositórios”, para cuidar de uma integração com outro serviço ou API podemos ter um “provider”. A depender de seu projeto, pode ser importante dividir responsabilidades para evitar poluição de código.
É só isso?
Sim, é só isso. Seguindo essa estrutura você consegue montar da mais básica até a mais complexa de uma API, com um código limpo, legível, bem organizado e escalável. A depender da aplicação, realmente não precisamos de muito mais.
É importante ainda que você esteja atento a alguns conceitos importantes por aqui: divisão de responsabilidades, dinamicidade e desacoplamento de funções.
Devemos dividir bem a responsabilidade de nossas classes e funções de forma que elas não façam nada além do que lhe é proposto. Isso faz com que nosso código esteja melhor organizado, e consequentemente facilita o nosso trabalho de desenvolvimento
Todas as funções e classes devem ser bastante dinâmicas e estarem preparadas para qualquer possível objetivo, evitando, por exemplo, repetição de código.
Tenha quantas rotas, controllers, e principalmente services forem necessários. Quando falamos de desacoplamento de funções queremos justamente diminuir a dependência de uma determinada ferramenta. Por que usar um repositório, por exemplo? Se você trocar de banco de dados, ajuste apenas as funções do seu repositório. Os services, que utilizam o repositório, não vão nem mesmo perceber a diferença de troca de banco de dados. Esse conceito pode ser aplicado em qualquer ponta da sua aplicação.
Perceba também que nem mesmo cito linguagens ou frameworks. Não faz diferença. Programação vai muito além da ferramenta, e é mesmo sobre o conceito por trás de sua estrutura.
Podemos ainda mais explorar esses conceitos, melhorando a nossa estrutura e sempre adaptando a realidade do nosso projeto.
Como você monta a sua API? Qual estrutura utiliza? Participe de nosso canal no Discord e conte para gente!