Níveis de maturidade de uma API REST
Por Guilherme Forte
No quadrinho acima, vemos uma representação da comunicação entre dois softwares. E APIs são exatamente uma das maneiras que mais se utiliza hoje para a comunicação entre sistemas. API vem de Application Programming Interface (Interface de Programação de Aplicação), um conceito para qualquer contexto de desenvolvimento de software quando o objetivo é a comunicação entre aplicativos através de uma interface.
Em um contexto WEB, onde a comunicação ocorre através da internet, temos as famosas APIs REST. Elas não deixam de ser APIs, porém utilizam o protocolo HTTP como meio de comunicação. Sua criação segue o modelo de arquitetura REST (Representational State Transfer – Transferência de Estado Representacional). Que nada mais é do que um conjunto de restrições usadas na construção de uma API desse tipo.
Mas quando se dá início no desenvolvimento de uma API REST, você já pode ter se perguntado, como estruturar os endpoints para escalabilidade e organização? Como garantir que a ela esteja utilizando os conceitos REST para evitar problemas? Como, por exemplo:
- Má utilização das ações HTTP, que podem gerar lentidões;
- Aumento de complexidade no código devido à má organização e tratamento de lógica para endpoints únicos. Isso gera custo maior no desenvolvimento e dificuldade de manutenção no código;
- Dificuldade de compreensão da estrutura para quem vai consumir API;
- Utilização de mais recursos de infraestrutura de forma desnecessária.
É preciso conhecer os níveis de maturidades existentes e aplicar seus conceitos garantindo que a API atinja a maturidade mais elevada. Para que, dessa forma, seja uma API REST rápida, robusta e escalável, agregando muitos benefícios a arquitetura onde está sendo implementada.
Níveis existentes de uma API REST
Uma API pode ter 4 níveis de maturidade, sendo o mais baixo o nível 0. Nele, serão escritos mais códigos, mais desorganização será encontrada, mais recursos serão utilizados para mantê-las, tendo possíveis problemas durante as requisições. Seguindo até o nível 3, onde a API estará completamente organizada, trazendo informações que ajudarão seu consumidor entender a estrutura dos recursos disponíveis.
Usando o conceito de abstração da orientação a objetos, podemos abstrair qualquer item do mundo real, seja físico ou virtual, para uma classe, com funções e atributos que a represente. Um recurso nada mais é que um objeto abstraído em uma API. Consideramos todo substantivo abstraído em forma de classe um recurso em uma API. Da mesma forma que organizamos nosso código utilizando boas práticas, como SOLID e o clean CODE, devemos pensar a melhor forma de disponibilizar os recursos de uma API. Para isso, devemos considerar o crescimento e o as necessidades das aplicações que irão consumi-la.
A seguir iremos passar por cada fase de maturidade de uma API REST.
API REST Maturidade 0
Na API com nível mais baixo de maturidade, encontramos apenas um verbo relacionado ao tipo de operação, o POST. Todas as URLs, sendo elas para ações de obter dados, atualizar dados, inserir e deletar, chamamos usando o POST. Outro ponto interessante nesse nível é que apenas existira uma URL publicado para acesso a API. Então caso seja feita uma requisição para obter todos os usuários de um aplicativo, teríamos o seguinte exemplo:
Da mesma forma, caso a requisição para essa API tenha como finalidade inserir um novo cliente, a URL utilizada seria a mesma, trocando alguns parâmetros. O programador teria que garantir no desenvolvimento que os processos requisitados fossem devidamente recebidos. Exemplo da URL para inserir usuários:
O problema de uma API nesse nível é que operações simples exigem muita documentação, pois a estrutura e suas relações não são claras, e os comandos são personalizados na URL. Com vários recursos disponíveis nessa API, o código pode se tornar inviável e difícil de escalar no futuro. Além disso, o desenvolvedor terá mais trabalho para gerenciar as ações necessárias dependendo da requisição.
Você também poderá encontrar um tráfego maior de dados desnecessários se a codificação não garantir corretamente como os dados devem ser entregues, o que pode influenciar muito na arquitetura, gerando lentidão e uma escalabilidade de recursos desnecessária caso a API REST esteja na nuvem.
API REST Maturidade 1
Nesse nível de maturidade as APIs se tornam mais organizadas. Uma URL é criada para cada recurso que será consumido. Um exemplo de URL nesse nível de maturidade pode ser visto abaixo:
Da mesma forma caso a requisição seja apenas para obter os dados específicos de um recurso a URL muda adicionando o identificador daquele recurso no final, como no exemplo:
Caso exista uma relação desse recurso com um sub recurso, teríamos o seguinte exemplo de endpoint:
Segundo Ian Robinson o nível de maturidade 1: “Combina com o método de lidar com uma complexidade dividindo e conquistando, quebrando um único endpoint com diversos serviços em múltiplos recursos.” (Fowler MARTIN, 2010, Tradução nossa)
Porém nesse nível ainda há apenas um tipo de operação, o POST. Também é importante garantir nesse nível que os nomes dos recursos estejam no plural, sem letras maiúsculas e utilizando um hífen para separação, como visto nos exemplos acima. Nota-se a existência de recursos raiz e sub recursos, neste caso os sub recursos são recursos que só existem a partir de outro, baseado na regra de negócio do sistema. Um recurso usuário pode ter como sub recurso um dia de treino, nesse exemplo o dia de treino só existe se um usuário existe e o planeja.
É interessante enxergar que nesse nível já podemos ter uma grande redução de problemas na arquitetura, como a remoção de complexidade no desenvolvimento, o que pode inclusive reduzir custos na criação das APIs.
API REST Maturidade 2
Ao criar uma tela de cadastro, é extremamente importante implementar operações simples como criar, atualizar, buscar e deletar recursos. Nesse nível de maturidade, você deve aplicar a mesma ideia com os verbos HTTP, que precisam ser suficientes para um CRUD (Create, Read, Update, Delete). Para cada ação de um recurso da API, você aplica verbos diferentes.
Verbos
Dentre as opções disponíveis baseadas na arquitetura REST, as mais conhecidas são as seguintes:
- POST: criação de um novo recurso, quando pensamos no recurso cliente, a ação realizada seria de criar um cliente;
- GET: buscar dados de um ou vários recursos;
- PUT: atualizar um recurso, por inteiro. Se um recurso cliente tem como informações os atributos nome, e-mail, data de nascimento e CPF, ao efetuar uma atualização passando o verbo PUT na requisição a API, todos os atributos do cliente devem ser enviados no corpo da requisição, mesmo aqueles que não serão atualizados;
- PATCH: atualizar um recurso, parcialmente. Seguindo o mesmo exemplo da ação PUT, você enviará apenas o atributo desejado no corpo da requisição para um cliente com seus atributos. Geralmente para atualização é a ação mais utilizada, pois utiliza menos dados para navegação no tráfego de informação;
- DELETE: remover um recurso. É importante saber que cada recurso tem um identificador único. E é esse identificador que deve ser enviado para API como um parâmetro, para que ela saiba qual é o recurso exato a ser deletado;
- HEAD: verificar se um recurso existe, evitando o mínimo de tráfego entre a requisição a recomendação do nível de maturidade 2 é a utilização do HEAD. Se o recurso existir apenas no código 200, o retorno vai informar sua existência. Em caso negativo, o código 404 vai informar a não existência do recurso;
- OPTIONS: verificar todos os tipos de operações disponíveis para um determinado recurso. Se um recurso tem operações de criação, busca e atualização, é nessa requisição que devem constar essas possibilidades. O consumidor da API deve saber exatamente todas as operações disponíveis para aquele recurso. Assim, caso a ação desejada não esteja disponível, o desenvolvedor poderá entrar em contato com o criador da API e solicitar a implementação dessa operação.
API mais legível
Para Ian Robinson, nesse nível temos: “Introdução de verbos padrões para lidar com situações similares da mesma forma, removendo variações desnecessárias.” (Fowler MARTIN, 2010, Tradução nossa).
Dessa forma, a API se torna muito mais legível para manutenções, reduz seu custo e fica mais fácil de consumir. Já sabemos, por exemplo, que ao fazer um GET, esperamos o retorno de dados. Isso ajuda inclusive na performance na transferência de dados.
API REST Maturidade 3
Atingimos o último nível de maturidade de uma API quando aplicamos o HATEOAS (Hypermedia as the Engine of Application State). Ian Robinson define o nível da seguinte forma: “Introduz a descoberta, promovendo um caminho de deixar o protocolo com uma auto documentação.” (Fowler MARTIN, 2010, Tradução nossa)
Como exemplo podemos imaginar a navegação em um Website. Um usuário deve saber para onde vai ao clicar em um link disponível na tela, sendo que todos os recursos do website ficam disponíveis para acesso com fácil entendimento para quem está navegando. A mesma ideia ocorre na API com esse nível de maturidade, porém aplicado ao sistema que está consumindo a mesma. Um exemplo seria, ao fazer uma inserção de um recurso teríamos que realizar a ação POST para a URL disponível pela API:
Ao mesmo tempo, no corpo dessa requisição, é necessário enviar os atributos desse cliente no formato que a API requisitar, como nome, idade, entre outros necessários.
O retorno dessa API nos níveis anteriores já trazia um status indicando que o novo cliente foi criado e, ao mesmo tempo, trazia todos os dados desse cliente, junto ao seu identificador único.
Com o HATEOAS aplicado nessa API o retorno também trará todas as ações possíveis para este recurso, junto as relações que este recurso tem com outros recursos e exemplos de como acessá-los.
Uma API no nível 3 de maturidade fornece auto documentação, facilidade de entendimento e reaproveitamento, sendo suficiente para o cliente da API ir navegando e descobrindo os recursos possíveis. Exatamente como um Website informa o usuário sobre os recursos possíveis para a tela que ele está navegando, uma API no nível 3 deve fazer o mesmo para o sistema que a está consumindo.
Afinal em que nível de maturidade construir sua API?
Ao analisar todos os níveis de maturidade existentes e aplicar todas as práticas para atingi-los, você garante que, ao construir uma API com nível 3 de maturidade, ela estará organizada, com endpoints fáceis de compreender, recursos fáceis de encontrar, escalabilidade garantida e uma auto documentação para seus recursos, facilitando assim a vida dos usuários que vão consumi-la e dos desenvolvedores que vão mantê-la. Você aplicará os conceitos REST e poderá afirmar com convicção que ela é uma API REST completa.
Por isso, se está pensando em construir APIs REST para comunicação entre os softwares de sua empresa, lembre-se que aplicar corretamente os níveis de maturidade vai ajudar a garantir que a complexidade de seu desenvolvimento e manutenção sejam menores. Isso afeta diretamente os custos, não apenas da mão de obra, como também de infraestrutura. Uma API madura “quase“ garante o uso dos recursos necessários.
Ainda que uma API madura traga enormes benefícios, fica o aviso que existem alguns sinais de esgotamento dessa tecnologia, como o overfetching e o underfetching. Porém novas tecnologias já existem para resolver esses esgotamentos, como ODATA da Microsoft e o GraphQL do Facebook. Então dependendo das necessidades, considere estudos dos desgastes e tecnologias citados, para assim poder garantir uma arquitetura muito bem estruturada. Mas isso é papo para outro artigo.
Bibliografia
MARTIN, Fowler. Richardson Maturity Model steps toward the Glory of REST. 2010. Disponível em: https://martinfowler.com/articles/richardsonMaturityModel.html
Guilherme Forte graduou-se em Gestão da Tecnologia da Informação e especializou-se em Engenharia de Software. Entusiasta na área de programação, possui mais de 5 anos de experiência profissional e adora aprender novas tecnologias e metodologias de desenvolvimento. No tempo livre, gosta de pegar seu violão e praticar um pouco.