Skip to content

theagoliveira/comics-api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Ā 

History

58 Commits
Ā 
Ā 
Ā 
Ā 
Ā 
Ā 
Ā 
Ā 
Ā 
Ā 
Ā 
Ā 
Ā 
Ā 
Ā 
Ā 
Ā 
Ā 
Ā 
Ā 
Ā 
Ā 

Repository files navigation

Spring ā€” Desenvolvendo uma API para gerenciamento de coleƧƵes de quadrinhos

Thiago Cavalcante, 02/02/2022

O cĆ³digo completo deste projeto pode ser acessado no GitHub.

SumƔrio


DescriĆ§Ć£o do sistema

Nosso objetivo Ć© desenvolver uma API REST1 onde podem ser cadastrados usuĆ”rios e quadrinhos. Nesse sistema, cada usuĆ”rio pode estar associado a mĆŗltiplos quadrinhos (sua coleĆ§Ć£o). As informaƧƵes desses quadrinhos sĆ£o obtidas com a API da Marvel, que fornece todos os detalhes de um quadrinho a partir do seu identificador Ćŗnico ā€” comicId. AlĆ©m do cadastro de usuĆ”rios e quadrinhos, o sistema que vamos desenvolver tambĆ©m possui duas funƧƵes de busca de informaƧƵes:

  • Lista de usuĆ”rios cadastrados, sem exibir as coleƧƵes de quadrinhos
  • ColeĆ§Ć£o de quadrinhos de um usuĆ”rio especĆ­fico, obtida a partir do seu id

Com relaĆ§Ć£o Ć  Ćŗltima funĆ§Ć£o de busca, hĆ” ainda uma outra caraterĆ­stica do sistema: sĆ£o atribuĆ­dos preƧos aos quadrinhos e eles recebem um desconto de acordo com o dia da semana e o seu cĆ³digo ISBN. Quando a busca Ć© realizada, os quadrinhos para os quais esse desconto Ć© aplicĆ”vel sĆ£o retornados com os preƧos atualizados.

InicializaĆ§Ć£o do projeto

Podemos utilizar a ferramenta Spring Initializr para criar um "esqueleto" do projeto que serĆ” o ponto de partida para a implementaĆ§Ć£o da API. A criaĆ§Ć£o desse projeto base requer algumas configuraƧƵes:

  • Ferramenta para gerenciamento de dependĆŖncias: vamos utilizar Gradle, por ser uma ferramenta mais moderna, mais rĆ”pida, e com uma sintaxe de configuraƧƵes mais simples e concisa;
  • Linguagem de programaĆ§Ć£o: a API serĆ” implementada em Java;
  • VersĆ£o do Spring Boot: usaremos a versĆ£o estĆ”vel mais recente do Spring Boot, 2.6.3;
  • Empacotamento: essa opĆ§Ć£o estĆ” ligada Ć  maneira como serĆ” feito o deployment do projeto, algo que estĆ” fora do escopo desse texto. Por isso, vamos deixar a opĆ§Ć£o Jar;
  • VersĆ£o Java: 11;
  • Metadados: ficam a critĆ©rio do desenvolvedor.

Escolha das dependĆŖncias

Para que a API REST possa ser implementada, Ć© necessĆ”rio incluir dependĆŖncias no nosso projeto base. As dependĆŖncias consistem em grupos de pacotes que adicionam uma ou mais funcionalidades em um projeto. O sistema que estamos desenvolvendo precisarĆ” das seguintes funcionalidades:

  • Capacidade de criaĆ§Ć£o de uma API REST, fundamental para o nosso projeto;
  • IntegraĆ§Ć£o com banco de dados, para armazenar e relacionar usuĆ”rios e quadrinhos;
  • IntegraĆ§Ć£o com a API da Marvel, para obtenĆ§Ć£o de informaƧƵes dos quadrinhos;
  • ValidaĆ§Ć£o das informaƧƵes de cadastro, para evitar problemas como campos vazios ou dados invĆ”lidos.

A tabela a seguir relaciona cada funcionalidade com as dependĆŖncias correspondentes:

FUNCIONALIDADE DEPENDƊNCIA
CriaĆ§Ć£o de API REST Spring Web
IntegraĆ§Ć£o com banco de dados Spring Data JPA, H2 Database
IntegraĆ§Ć£o com API da Marvel OpenFeign
ValidaĆ§Ć£o de informaƧƵes Validation

AlĆ©m das dependĆŖncias listadas, todos os projetos gerados com o Spring Initializr possuem tambĆ©m a dependĆŖncia para criaĆ§Ć£o de testes automatizados, que tambĆ©m vamos usar ao longo do projeto.

Antes de iniciar a implementaĆ§Ć£o, Ć© importante destacar a escolha do banco de dados para a API que estamos construindo. O H2 Ć© um banco de dados SQL open-source escrito em Java que Ć© embutido no projeto e nĆ£o necessita de nenhuma instalaĆ§Ć£o adicional. Por ser leve e rĆ”pido, esse banco de dados geralmente Ć© utilizado durante a fase de desenvolvimento, para fins de teste e prototipaĆ§Ć£o. Outra funcionalidade interessante do H2 Ć© o console que pode ser acessado pelo navegador para exploraĆ§Ć£o do banco de dados.

Por padrĆ£o, os dados armazenados em um banco H2 sĆ£o perdidos sempre que a aplicaĆ§Ć£o Ć© reiniciada. Esse comportamento pode ser modificado para que os dados sejam armazenados em um arquivo no sistema. No entanto, em um ambiente de produĆ§Ć£o onde a aplicaĆ§Ć£o Ć© utilizada pelos seus clientes, recomenda-se usar um banco de dados mais robusto, com funcionalidades extras que podem ser mais adequadas para a estrutura, tipo e quantidade de informaƧƵes que se deseja armazenar.

O PadrĆ£o MVC e a organizaĆ§Ć£o do projeto

Os projetos de aplicaƧƵes web e APIs no Spring Framework seguem o PadrĆ£o MVC (Model-View-Controller). Nesse padrĆ£o, as requisiƧƵes feitas Ć  aplicaĆ§Ć£o sĆ£o direcionadas para os controladores adequados. Os controladores podem entĆ£o acessar o modelo da aplicaĆ§Ć£o (contĆ©m os dados e a lĆ³gica do sistema) para obter e processar informaƧƵes para essa requisiĆ§Ć£o, caso necessĆ”rio. Por fim, a aplicaĆ§Ć£o pode retornar uma visualizaĆ§Ć£o para o cliente, o resultado daquela requisiĆ§Ć£o. No caso de uma API, essa visualizaĆ§Ć£o Ć© apresentada no formato JSON (JavaScript Object Notation), muito usado na troca de informaƧƵes entre sistemas.

Ɖ comum separar os diferentes componentes de um projeto que segue o padrĆ£o MVC em diferentes pastas, para manter o projeto organizado. Para essa API, vamos utilizar as seguintes pastas:

  • models: classes de entidades (usuĆ”rios e quadrinhos);
  • controllers: classes de controladores;
  • repositories: classes de repositĆ³rios, responsĆ”veis pela obtenĆ§Ć£o de informaƧƵes do banco de dados;
  • services: classes de serviƧos, que contĆ©m as regras de negĆ³cio e atuam como elementos intermediĆ”rios entre os controladores e os repositĆ³rios;
  • outras pastas podem ser criadas de acordo com as necessidades do projeto.

UtilizaĆ§Ć£o de testes automatizados

A implementaĆ§Ć£o dessa API busca seguir um modelo de desenvolvimento baseado em TDD (Test-Driven Development). Nesse tipo de processo, os testes automatizados sĆ£o escritos primeiro, de acordo com o comportamento esperado para o componente que estĆ” sendo testado, e entĆ£o o cĆ³digo Ć© implementado e modificado atĆ© que os testes sejam executados com sucesso. Os testes se dividem em:

  • Testes unitĆ”rios: sĆ£o usados para testar a funcionalidade pontual de um determinado componente, utilizando mocks (objetos "de mentira" com comportamento prĆ©-definido) para substituir objetos dos quais o componente depende para funcionar. SĆ£o testes mais rĆ”pidos, criados em maior quantidade para testar casos de sucesso e falha nas diversas operaƧƵes realizadas pelo componente;
  • Testes de integraĆ§Ć£o: sĆ£o usados para testar a integraĆ§Ć£o entre diversos componentes no sistema, utilizando objetos reais com o comportamento que foi implementado no cĆ³digo. SĆ£o testes mais lentos, pois precisam de todo o contexto da aplicaĆ§Ć£o para rodar (como se ela estivesse de fato sendo acessada por um cliente), podendo inclusive realizar alteraƧƵes no banco de dados. Esses testes sĆ£o, portanto, criados em menor quantidade.

Os testes sĆ£o feitos utilizando os utilizando os frameworks de teste JUnit e Mockito. O Mockito permite a criaĆ§Ć£o dos mocks citados previamente, alĆ©m de outras funcionalidades. Os testes em controladores, assim como os testes de integraĆ§Ć£o, necessitam do contexto da aplicaĆ§Ć£o para que seja possĆ­vel testar as requisiƧƵes HTTP enviadas pelo cliente.

InĆ­cio da implementaĆ§Ć£o: UsuĆ”rios

CriaĆ§Ć£o da entidade UsuĆ”rio

Vamos comeƧar a implementaĆ§Ć£o com a parte mais fundamental do nosso projeto: o cadastro e busca de usuĆ”rios. O primeiro passo Ć© criar uma classe para os usuĆ”rios, contendo os campos de acordo com as especificaƧƵes dadas na tabela a abaixo, onde um campo Ćŗnico significa que o valor daquele campo nĆ£o pode se repetir em mais de um usuĆ”rio (p. ex., nĆ£o Ć© permitido cadastrar dois usuĆ”rios com o mesmo CPF).

CAMPO TIPO RESTRIƇƕES
Id Long ƚnico e gerado automaticamente
Nome String ObrigatĆ³rio
Email String ObrigatĆ³rio, em um formato vĆ”lido e Ćŗnico
CPF String ObrigatĆ³rio, vĆ”lido e Ćŗnico
Data de nascimento String ObrigatĆ³ria e no formato DD/MM/AAAA
Quadrinhos da coleĆ§Ć£o2 Set<Comic>

Para realizar o transporte de informaƧƵes dentro da API vamos usar o padrĆ£o DTO (Data Transfer Object). Nesse padrĆ£o, sĆ£o criadas uma ou mais classes de DTO alĆ©m das classes de entidades. Os DTOs sĆ£o POJOs (Plain Old Java Objects) que possuem apenas os atributos necessĆ”rios para recebimento ou envio de informaƧƵes para o cliente. Dessa forma, a validaĆ§Ć£o dos dados deve ocorrer nos DTOs que sĆ£o usados para receber informaƧƵes. A implementaĆ§Ć£o do padrĆ£o DTO tambĆ©m requer a criaĆ§Ć£o de classes de conversores para que um DTO possa ser transformado em um objeto de entidade e vice-versa.

Nessa API que estamos desenvolvendo, existem duas classes de DTOs: uma para usuĆ”rios e outra para quadrinhos. A classe de DTO de usuĆ”rio Ć© usada tanto para recebimento (cadastro) quanto para envio (busca) de informaƧƵes e ela nĆ£o possui o campo de quadrinhos da coleĆ§Ć£o. A classe de DTO de quadrinho Ć© usada apenas no envio de informaƧƵes, pois o cadastro Ć© feito com as informaƧƵes da API da Marvel.

Para transformar a classe de usuĆ”rio em uma entidade do projeto, garantir a geraĆ§Ć£o automĆ”tica do id, e aplicar as validaƧƵes dos dados no DTO, precisamos utilizar anotaƧƵes. As anotaƧƵes sĆ£o comandos iniciados com @ que podem ser usados em vĆ”rios elementos do cĆ³digo para adicionar funcionalidades sem escrever cĆ³digo adicional.

As dependĆŖncias do Spring introduzem diferentes anotaƧƵes com propĆ³sitos variados. Por exemplo, um atributo de texto anotado com @NotBlank Ć© considerado invĆ”lido se ele for nulo ou se seu tamanho for zero, ignorando espaƧos em branco no inĆ­cio e no final. O bloco abaixo mostra a classe de usuĆ”rio DTO e a entidade de usuĆ”rio:

                                |   @Entity
public class UserDTO {          |   public class User {
                                |       @Id
                                |       @GeneratedValue
    private Long id;            |       private Long id;
                                |
    @NotBlank                   |
    private String name;        |       private String name;
                                |
    @NotBlank                   |
    @Email                      |
    @UniqueEmail                |
    private String email;       |       private String email;
                                |
    @NotBlank                   |
    @CPF                        |
    @UniqueCPF                  |
    private String cpf;         |       private String cpf;
                                |
    @NotBlank                   |
    @BrazilDateFormat           |
    private String birthDate;   |       private String birthDate;
}                               |   }

Podemos observar algumas anotaƧƵes convenientes para o projeto, como @Email e @CPF para validaĆ§Ć£o desses dados, e a anotaĆ§Ć£o @GeneratedValue, que garante a geraĆ§Ć£o automĆ”tica dos ids de usuĆ”rios. As anotaƧƵes @BrazilDateFormat, @UniqueEmail e @UniqueCPF sĆ£o validadores customizados, criados exclusivamente para atender aos requisitos do projeto.

DefiniĆ§Ć£o das aƧƵes e URLs da API

Com a entidade de usuƔrio devidamente criada, podemos comeƧar a implementar as aƧƵes do sistema relativas aos usuƔrios. Tais aƧƵes devem ser declaradas no controlador de usuƔrio, responsƔvel por receber as requisiƧƵes do cliente. Para que essas aƧƵes sejam executadas, Ʃ necessƔrio que existam URLs mapeadas atƩ elas, associadas a comandos HTTP.

Em geral, uma API REST segue uma convenĆ§Ć£o de URLs e comandos HTTP que implementam as funƧƵes CRUD (Create, Read, Update, Delete) para manipulaĆ§Ć£o de um determinado recurso do sistema. A tabela a seguir mostra como essa convenĆ§Ć£o se aplica a usuĆ”rios em uma API REST:

COM. HTTP URL AƇƃO PROPƓSITO
GET /users index Lista com todos os usuƔrios
GET /users/1 show InformaƧƵes do usuƔrio (id: 1)
POST /users create Criar um usuƔrio
PATCH /users/1 update Atualizar as informaƧƵes do usuƔrio (id: 1)
DELETE /users/1 destroy Remover o usuƔrio (id: 1)

Tomando a tabela acima como base, podemos criar uma tabela das aƧƵes que serĆ£o implementadas nesse projeto:

COM. HTTP URL AƇƃO PROPƓSITO
GET /users index Lista com todos os usuƔrios, sem as coleƧƵes
POST /users create Criar um usuƔrio
GET /users/id/comics userComics Lista com quadrinhos do usuƔrio (id: id)
POST /comics/comicId create Criar um quadrinho a partir do comicId

No cĆ³digo do controlador, o mapeamento da URL e a associaĆ§Ć£o com o comando HTTP sĆ£o feitos com o uso de anotaƧƵes. O nome da aĆ§Ć£o Ć© usado na declaraĆ§Ć£o do mĆ©todo que vai receber aquela requisiĆ§Ć£o. O bloco a seguir mostra um cabeƧalho simplificado para a aĆ§Ć£o create:

@PostMapping(value = "/users") // Esta anotacao mapeia a URL e o comando HTTP
public void create(...) {      // (POST) de uma vez so.
    ...
}

Cadastro dos usuƔrios no sistema

Para cadastrar os usuĆ”rios no sistema, o controlador deve receber do cliente uma requisiĆ§Ć£o contendo os dados que a serem cadastrados (nome, email etc.). Esses dados serĆ£o armazenados em um objeto DTO, que serĆ” validado pelo sistema de acordo com as restriƧƵes citadas anteriormente. Com todas as informaƧƵes validadas, o DTO serĆ” convertido para um objeto de entidade e enviado ao serviƧo de usuĆ”rio para ser salvo no banco de dados. Para salvar o objeto no banco de dados, o serviƧo utiliza o repositĆ³rio de usuĆ”rio. A imagem a seguir ilustra esse processo:

{width=100%}

Caso as informaƧƵes estejam vĆ”lidas e o processo ocorra sem problemas, a API retorna o status com cĆ³digo HTTP 201 para o cliente, o que significa que a criaĆ§Ć£o do recurso (usuĆ”rio) ocorreu com sucesso. Caso exista algum problema com as informaƧƵes recebidas, a API retorna o status com cĆ³digo HTTP 400, o que significa que houve uma solicitaĆ§Ć£o invĆ”lida. Nesses casos onde existe um erro, o cliente recebe uma mensagem informando o erro ocorrido. Ɖ importante destacar que existem duas classes de erros HTTP, a depender do cĆ³digo numĆ©rico:

  • Erros de cliente (4xx): quando o cliente faz uma requisiĆ§Ć£o invĆ”lida, que possui dados invĆ”lidos ou que nĆ£o pode ser atendida pela aplicaĆ§Ć£o;
  • Erros de servidor (5xx): quando a requisiĆ§Ć£o estĆ” correta, mas o servidor nĆ£o consegue atendĆŖ-la por diversos motivos (p. ex., falhas de implementaĆ§Ć£o).

Para que o cliente receba uma mensagem de erro customizada lhe informando o que aconteceu, Ʃ necessƔrio implementar um ou mais exception handlers na API. Este componente Ʃ chamado sempre que ocorre um erro na API e tem como responsabilidade criar uma resposta adequada para o cliente.

A implementaĆ§Ć£o do handler dessa API pode tratar diferentes tipos de erros e possui um mĆ©todo de tratamento padrĆ£o, caso o erro ocorrido nĆ£o se encaixe em nenhum dos outros tipos. Dessa forma, garante-se que o cliente vai receber uma mensagem de erro amigĆ”vel, mesmo quando o erro nĆ£o possuir um tratamento especĆ­fico definido. O bloco a seguir compara as mensagens de erro retornadas pela API antes e depois da implementaĆ§Ć£o do handler:

// SEM HANDLER
"timestamp": "2022-01-28T21:14:54.299+00:00",
"status": 400,
"error": "Bad Request",
"path": "/users"

// COM HANDLER
"httpValue": 400,
"httpError": "Bad Request",
"message": "Validation failed with 2 errors.",
"errors": {                            // Agora o cliente sabe por que
    "name": "Name cannot be blank.",   // sua requisicao falhou. Ele tentou
    "email": "Email cannot be blank."  // cadastrar um usuario sem nome e
}                                      // sem email.

As mensagens foram obtidas com o software Postman. No repositĆ³rio, Ć© possĆ­vel encontrar um arquivo do Postman com as requisiƧƵes usadas durante o desenvolvimento da API.

Busca de usuƔrios cadastrados

Na busca de usuĆ”rios, o cliente envia uma requisiĆ§Ć£o vazia usando URL e comando HTTP apropriados. Ao receber a requisiĆ§Ć£o, o controlador de usuĆ”rio pede a lista de usuĆ”rios ao serviƧo, o qual aciona o repositĆ³rio para a obtenĆ§Ć£o das informaƧƵes do banco de dados. A lista Ć© retornada ao controlador que a converte em uma lista de DTOs de usuĆ”rios, objetos mais simples que nĆ£o contĆ©m a coleĆ§Ć£o de quadrinhos de cada usuĆ”rio. Essa lista Ć© entĆ£o enviada ao cliente no formato JSON. A imagem a seguir ilustra esse processo:

{width=100%}

AdiĆ§Ć£o dos quadrinhos Ć  API

CriaĆ§Ć£o da entidade Quadrinho

Assim como no caso dos usuƔrios, temos uma tabela com os campos e restriƧƵes para os quadrinhos:

CAMPO TIPO RESTRIƇƕES
ComicId Long ƚnico, relativo Ơ API da Marvel
TĆ­tulo String ObrigatĆ³rio
PreƧo BigDecimal ObrigatĆ³rio
Possui desconto Boolean ObrigatĆ³rio
Autores Set<String> ObrigatĆ³rios
ISBN String ObrigatĆ³rio e Ćŗnico
DescriĆ§Ć£o String ObrigatĆ³ria
UsuƔrios associados Set<User>

Antes de continuar com a implementaĆ§Ć£o, devemos observar a relaĆ§Ć£o que existe entre usuĆ”rios e quadrinhos. Deseja-se que os vĆ”rios usuĆ”rios possam ter vĆ”rios quadrinhos nas suas coleƧƵes. Isso significa que um mesmo quadrinho pode estar listado em mais de uma coleĆ§Ć£o e que um usuĆ”rio pode estar associado a mais de um quadrinho. Como os quadrinhos devem ser Ćŗnicos no banco de dados por causa da restriĆ§Ć£o sobre o ISBN, a API deve estabelecer uma relaĆ§Ć£o muitos-para-muitos entre as duas entidades. Dessa forma, Ć© criada uma nova tabela no banco de dados para associar os identificadores de usuĆ”rios e quadrinhos, sem que haja a duplicaĆ§Ć£o de objetos.

A relaĆ§Ć£o entre os quadrinhos e a API da Marvel

Diferentemente dos usuĆ”rios, as informaƧƵes dos quadrinhos nĆ£o sĆ£o fornecidas pelo cliente no corpo da requisiĆ§Ć£o. Essas informaƧƵes sĆ£o extraĆ­das da API da Marvel, de acordo com o comicId fornecido pelo cliente. A pĆ”gina de documentaĆ§Ć£o interativa da API da Marvel contĆ©m todas as requisiƧƵes que podem ser feitas e aquilo que elas retornam. Para o caso da API que estamos desenvolvendo, esta Ć© a requisiĆ§Ć£o de interesse:

GET /v1/public/comics/{comicId}                 // Fetches a single comic by id.

A pĆ”gina oferece a possibilidade de testar a API apĆ³s a realizaĆ§Ć£o de um cadastro para obter as chaves pĆŗblica e privada para autenticaĆ§Ć£o. Essas chaves nĆ£o devem ficar expostas no cĆ³digo, e por isso sĆ£o armazenadas como variĆ”veis de ambiente no sistema operacional. Ɖ possĆ­vel acessar as variĆ”veis de ambiente dentro da aplicaĆ§Ć£o usando a notaĆ§Ć£o de propriedades do Spring. Por exemplo, a variĆ”vel de ambiente MARVEL_API_PUBLIC_KEY pode ser acessada como marvel.api.public.key. Os valores das chaves sĆ£o injetados nas variĆ”veis do controlador usando a anotaĆ§Ć£o @Value:

@Value("${marvel.api.public.key}")
private String pubKey;

@Value("${marvel.api.private.key}")
private String privKey;

A pĆ”gina de autorizaĆ§Ć£o da API da Marvel explica os procedimentos e os parĆ¢metros de requisiĆ§Ć£o que devem ser usados para fazer uma chamada a partir de um servidor de uma aplicaĆ§Ć£o. Devem ser enviados uma timestamp, a chave pĆŗblica e um hash MD5 criado a partir do timestamp e das chaves. O bloco a seguir mostra a requisiĆ§Ć£o completa:

GET /v1/public/comics/{comicId}?ts=timestamp&apiKey=chavePublica&hash=hashMD5

Observando o resultado da requisiĆ§Ć£o Ć  API da Marvel, vemos que sĆ£o retornadas diversas informaƧƵes, muito mais do que aquelas que precisamos para a API que estamos desenvolvendo. ApĆ³s uma anĆ”lise dos campos que sĆ£o retornados, se chamarmos esses dados de response, podemos estabelecer uma relaĆ§Ć£o entre os campos dos quadrinhos e os dados da Marvel de acordo com a tabela a seguir:

CAMPO DO QUADRINHO DADOS DA MARVEL
ComicId response.data.results[0].id
TĆ­tulo response.data.results[0].title
PreƧo response.data.results[0].prices[].price
Autores response.data.results[0].creators.items[].name
ISBN response.data.results[0].isbn
DescriĆ§Ć£o response.data.results[0].description

Nos dados acima, os campos results, prices e items sĆ£o listas de elementos. Com relaĆ§Ć£o ao campo results, existem chamadas que podem retornar mais de um quadrinho. Esse nĆ£o Ć© o caso para a chamada que estamos utilizando, pois nĆ£o existe mais de um quadrinho com o mesmo id. Portanto, o campo results sempre terĆ” um Ćŗnico elemento na sua lista.

Com relaĆ§Ć£o aos campos restantes, um quadrinho pode ter mais de um preƧo (impresso e/ou digital) e mais de um autor (roteirista, desenhista etc.). Na API que estamos desenvolvendo, vamos considerar o preƧo impresso e vamos retornar o conjunto com todos os autores e seus papĆ©is na produĆ§Ć£o do quadrinho.

Spring Cloud OpenFeign: consumindo a API da Marvel

Para conseguirmos acessar a API da Marvel a partir do projeto, Ć© necessĆ”rio construir o cĆ³digo que vai fazer a requisiĆ§Ć£o HTTP para a URL adequada, com os parĆ¢metros corretos, e converter os dados recebidos para objetos que possam ser utilizados dentro do sistema. A realizaĆ§Ć£o desse processo pode ser simplificada com a utilizaĆ§Ć£o do projeto Spring Cloud OpenFeign.

Com o Feign, Ć© possĆ­vel criar clientes para APIs externas de uma forma declarativa, sem a necessidade de implementar de fato o cĆ³digo que farĆ” a requisiĆ§Ć£o. O bloco a seguir mostra o cliente Feign com a declaraĆ§Ć£o da aĆ§Ć£o que precisamos para obter as informaƧƵes dos quadrinhos da API da Marvel:

@FeignClient(value = "marvelApi", url = "https://gateway.marvel.com")
public interface MarvelApiClient {
    @GetMapping(value = "/v1/public/comics/{comicId}",
                produces = "application/json")
    MarvelResponse getComicByComicId(@PathVariable("comicId") Long comicId,
                                     @RequestParam("ts") String ts,
                                     @RequestParam("apikey") String apikey,
                                     @RequestParam("hash") String hash);
}

Comparando esse cĆ³digo com a requisiĆ§Ć£o na seĆ§Ć£o anterior, podemos ver de maneira bem clara como cada elemento da chamada Ć© levado em consideraĆ§Ć£o no cĆ³digo do cliente Feign.

Se observarmos o tipo de dado que a aĆ§Ć£o retorna, vemos que Ć© do tipo MarvelResponse. As informaƧƵes enviadas pela API da Marvel chegam no formato JSON e uma forma de converter esses dados para objetos dentro do projeto Ć© criar uma estrutura de classes que possua a mesma estrutura dos dados que sĆ£o recebidos, utilizando apenas os campos relevantes e com os mesmos nomes. O quadro a seguir mostra essa estrutura:

MarvelResponse
 ā””ā”€ MarvelData data
     ā””ā”€ List<MarvelResult> results
         ā”œā”€ Long id
         ā”œā”€ String title
         ā”œā”€ String isbn
         ā”œā”€ String description
         ā”œā”€ List<MarvelPrice> prices
         ā”‚   ā”œā”€ String type
         ā”‚   ā””ā”€ BigDecimal price
         ā””ā”€ MarvelCreators creators
             ā””ā”€ List<MarvelCreatorsItem> items
                 ā”œā”€ String name
                 ā””ā”€ String role

Com o cliente Feign implementado e as classes Marvel criadas, estamos prontos para consumir a API e cadastrar os quadrinhos no banco de dados.

Cadastro dos quadrinhos no sistema

Para cadastrar um novo quadrinho no sistema, o cliente deve enviar uma requisiĆ§Ć£o contendo o identificador do quadrinho (comicId), que serĆ” usado para obter as informaƧƵes da Marvel, e o identificador do usuĆ”rio (userId) que serĆ” associado ao quadrinho (ou seja, o quadrinho farĆ” parte da coleĆ§Ć£o dele). Na API que estamos desenvolvendo, o comicId Ć© enviado como uma variĆ”vel de caminho e o userId Ć© enviado como um parĆ¢metro de requisiĆ§Ć£o, de acordo com o bloco abaixo:

GET /comics/{comicId}?userId={userId}

Ao receber a requisiĆ§Ć£o, o controlador de quadrinhos imediatamente verifica se existe um usuĆ”rio com o id userId cadastrado no banco de dados, pois nĆ£o deve ser possĆ­vel cadastrar um quadrinho sem associĆ”-lo a um usuĆ”rio vĆ”lido. Caso esse usuĆ”rio nĆ£o exista, o cliente recebe um erro informando o ocorrido. Esse erro, assim como todos os outros jĆ” mencionados, Ć© criado a partir da implementaĆ§Ć£o de um exception handler customizado.

Uma vez que Ć© verificada a presenƧa do usuĆ”rio, Ć© realizada a requisiĆ§Ć£o Ć  API da Marvel com o cliente Feign, como discutido na seĆ§Ć£o anterior. O resultado da requisiĆ§Ć£o Ć© armazenado em um objeto MarvelResponse, o qual Ć© convertido para um Comic. Diferentemente dos usuĆ”rios, a validaĆ§Ć£o das informaƧƵes Ć© feita no objeto de entidade, jĆ” que o DTO de quadrinhos nĆ£o Ć© usado no recebimento de informaƧƵes do cliente.

A validaĆ§Ć£o do DTO de usuĆ”rio Ć© feita assim que o controlador recebe os dados JSON da requisiĆ§Ć£o. No caso dos quadrinhos, como nenhum JSON Ć© recebido, a validaĆ§Ć£o Ć© feita de outra maneira apĆ³s o consumo da API da Marvel, com o uso da classe Validator. Ao validar um objeto dessa forma, o sistema recebe um conjunto de constraint violations (violaƧƵes de restriĆ§Ć£o) que podem ser usadas para gerar um erro no sistema e alertar o cliente sobre os problemas.

No caso de nĆ£o existir nenhuma violaĆ§Ć£o, Ć© feita uma checagem do ISBN obtido da Marvel. NĆ£o desejamos ter vĆ”rios quadrinhos com o mesmo ISBN no banco de dados. Para evitar que isso aconteƧa, o sistema verifica se o ISBN obtido jĆ” estĆ” cadastrado no sistema ou nĆ£o. A partir daĆ­ existem trĆŖs possibilidades:

  1. NĆ£o existe nenhum quadrinho com esse ISBN cadastrado, o que significa que o cadastro do quadrinho e a atualizaĆ§Ć£o da coleĆ§Ć£o do usuĆ”rio podem ser realizados normalmente.
  2. JĆ” existe um quadrinho com esse ISBN cadastrado e o seu identificador Ć© igual ao comicId fornecido na requisiĆ§Ć£o, ou seja, o cliente estĆ” tentando cadastrar o mesmo quadrinho novamente. Nesse caso, Ć© feita apenas uma atualizaĆ§Ć£o da coleĆ§Ć£o do usuĆ”rio com esse quadrinho e ele continua cadastrado normalmente no sistema, sem ocasionar duplicaĆ§Ć£o do seu objeto no banco. Isso permite que vĆ”rios usuĆ”rios tenham um mesmo quadrinho em sua coleĆ§Ć£o (requisiƧƵes com o mesmo comicId, mas userIds diferentes).
  3. JĆ” existe um quadrinho com esse ISBN cadastrado e o seu identificador Ć© diferente do comicId fornecido na requisiĆ§Ć£o. Nesse caso, existe uma divergĆŖncia de informaƧƵes na API da Marvel (dois quadrinhos com o mesmo ISBN e comicIds diferentes) e o usuĆ”rio recebe um erro.

A imagem a seguir ilustra o processo de cadastro de um quadrinho (fluxo: caminho azul > caminho verde > caminho amarelo):

{width=100%}

Com a implementaĆ§Ć£o dos cadastros de usuĆ”rios e quadrinhos terminada, podemos finalizar a implementaĆ§Ć£o da API com a busca das coleƧƵes de quadrinhos dos usuĆ”rios.

Busca de coleƧƵes de quadrinhos

A busca de coleƧƵes de quadrinhos de usuĆ”rios tem muitas semelhanƧas com a busca dos usuĆ”rios cadastrados no sistema. Apesar de retornar uma lista de quadrinhos, essa funĆ§Ć£o tambĆ©m estĆ” declarada no controlador de usuĆ”rio, pois se refere a um procedimento que depende de um objeto de usuĆ”rio para ser realizado. A requisiĆ§Ć£o enviada pelo cliente tambĆ©m nĆ£o precisa de um corpo com informaƧƵes em JSON, porĆ©m Ć© necessĆ”rio enviar o identificador do usuĆ”rio (userId) do qual se deseja receber a coleĆ§Ć£o.

Uma vez recebida a requisiĆ§Ć£o do cliente, o controlador verifica se o usuĆ”rio desejado realmente existe e entĆ£o pede a lista de quadrinhos ao serviƧo de usuĆ”rio. Na busca de usuĆ”rios, o repositĆ³rio jĆ” retorna a lista para o serviƧo, que a repassa para o controlador. Na busca de quadrinhos, por outro lado, o repositĆ³rio retorna o objeto do usuĆ”rio e o serviƧo obtĆ©m a lista de quadrinhos a partir desse objeto. Vale lembrar que essa lista de quadrinhos estĆ” disponĆ­vel graƧas Ć  forma como foi implementada a associaĆ§Ć£o muitos-para-muitos entre as duas entidades.

Em posse da lista de quadrinhos, o serviƧo se encarrega de aplicar as regras de negĆ³cio da aplicaĆ§Ć£o. Como dito no inĆ­cio do texto, a depender do dia da semana e do Ćŗltimo dĆ­gito do ISBN, cada quadrinho pode receber um desconto de 10% sobre o seu preƧo. A tabela a seguir detalha essa lĆ³gica:

ƚLTIMO DƍGITO DO ISBN DIA DA SEMANA COM DESCONTO
0 e 1 Segunda-feira
2 e 3 TerƧa-feira
4 e 5 Quarta-feira
6 e 7 Quinta-feira
8 e 9 Sexta-feira

O serviƧo entĆ£o altera o atributo de desconto e o preƧo de cada objeto de quadrinho de acordo com essas regras e envia a lista para o controlador, que a redireciona para o conversor de quadrinhos. Dessa forma, o DTO criado a partir do objeto de quadrinho jĆ” Ć© construĆ­do com o preƧo modificado. A imagem abaixo ilustra este processo:

{width=100%}

Com isso, chegamos ao final da implementaĆ§Ć£o da API para gerenciamento de coleƧƵes de quadrinhos. Esperamos que esse conteĆŗdo tenha trazido aprendizado e despertado o interesse de construir novos projetos com Java e Spring Framework. Deixamos aqui a sugestĆ£o de reproduzir esse projeto e atualizĆ”-lo com novas funcionalidades. Como vocĆŖ expandiria esse projeto? O que poderia ser melhor ou diferente? AtĆ© a prĆ³xima!

ReferĆŖncias

As principais referĆŖncias usadas na produĆ§Ć£o desse texto foram a documentaĆ§Ć£o da API do Java 11 e o site Baeldung, que possui um extenso conteĆŗdo sobre Java e Spring Framework, sempre com uma abordagem prĆ”tica e repleta de exemplos. A seguir, listamos alguns artigos que oferecem uma cobertura mais detalhada sobre alguns dos assuntos tratados no texto:

Deixamos tambĆ©m uma menĆ§Ć£o honrosa para a ferramenta Gerador de CPF do site 4Devs, que auxiliou bastante na criaĆ§Ć£o de testes com CPFs vĆ”lidos.

Footnotes

  1. De uma forma simplificada, uma API REST funciona como um site na Internet. No entanto, ao ser acessada, ela retorna um conjunto de informaƧƵes estruturadas que podem ser utilizadas por outros sistemas. VocĆŖ pode acessar este link no navegador para ver os dados fornecidos por uma API que gera palavras aleatĆ³rias em inglĆŖs. ā†©

  2. A implementaĆ§Ć£o da lista de quadrinhos dos usuĆ”rios serĆ” realizada em conjunto com a implementaĆ§Ć£o da entidade Quadrinho. ā†©

About

REST API for managing comic collections

Topics

Resources

Stars

Watchers

Forks

Languages