... | @@ -38,4 +38,139 @@ Diante disso, os pacotes do projeto estão divididos da forma abaixo: |
... | @@ -38,4 +38,139 @@ Diante disso, os pacotes do projeto estão divididos da forma abaixo: |
|
* :file_folder: `repository`: Implementa a lógica de acesso ao banco de dados para buscar e persistir dados.
|
|
* :file_folder: `repository`: Implementa a lógica de acesso ao banco de dados para buscar e persistir dados.
|
|
* :file_folder: `service`: Classes que implementam as interfaces em _service\*.
|
|
* :file_folder: `service`: Classes que implementam as interfaces em _service\*.
|
|
* :file_folder: `interfaces`: Implementação da lógica de negócio (interfaces). Se necessário buscar ou salvar dados, delega isso para a camada de _repository\*
|
|
* :file_folder: `interfaces`: Implementação da lógica de negócio (interfaces). Se necessário buscar ou salvar dados, delega isso para a camada de _repository\*
|
|
* :file_folder: `util`: Classes e métodos utilitários (ex.: formatação de datas) |
|
* :file_folder: `util`: Classes e métodos utilitários (ex.: formatação de datas)
|
|
\ No newline at end of file |
|
|
|
|
|
## Padrões de código
|
|
|
|
|
|
|
|
### Nomenclatura de classes
|
|
|
|
|
|
|
|
Classes do projeto devem ser nomeadas em inglês e seguir o padrão **PascalCase**, ou seja, devem iniciar com letra maiúscula e cada palavra ou abreviatura no meio da frase também deve iniciar com letra maiúscula. Por exemplo:
|
|
|
|
|
|
|
|
* UserController :heavy_check_mark:
|
|
|
|
* User_Controller :x:
|
|
|
|
* Usercontroller :x:
|
|
|
|
* userController :x:
|
|
|
|
|
|
|
|
Além disso, com exceção do pacote model, os nomes das classes devem refletir o pacote onde elas estão localizadas, conforme o exemplo abaixo:
|
|
|
|
|
|
|
|
* :file_folder: config: ExemploConfig
|
|
|
|
* :file_folder: controller: ExemploController
|
|
|
|
* :file_folder: dt.request: ExemploRequest
|
|
|
|
* :file_folder: dt.response: ExemploResponse
|
|
|
|
* :file_folder: exception: ExemploException
|
|
|
|
* :file_folder: repository: ExemploRepository
|
|
|
|
* :file_folder: service: ExemploService
|
|
|
|
* :file_folder: util: ExemploUtil
|
|
|
|
|
|
|
|
### Nomenclatura de variáveis
|
|
|
|
|
|
|
|
Variáveis devem ser nomeadas em inglês e seguir o padrão **camelCase**, ou seja, devem iniciar com letra minúscula e cada palavra ou abreviatura no meio da frase deve iniciar com letra maiúscula. Por exemplo:
|
|
|
|
|
|
|
|
* isAdmin :heavy_check_mark:
|
|
|
|
* isadmin :x:
|
|
|
|
* is_Admin :x:
|
|
|
|
* IsAdmin :x:
|
|
|
|
|
|
|
|
A exceção são as variáveis constantes (que devem ser estáticas e finais) e enums, que devem seguir o padrão **SCREAMING_SNAKE_CASE**, onde as palavras no meio da frase são separadas por *underscores* (_) e as letras devem ser todas maiúsculas. Por exemplo: API_BASE_URL
|
|
|
|
|
|
|
|
Além disso, para criar um código limpo e fácil de entender e realizar a manutenção depois, sempre nomeie as variáveis de forma que fique claro o seu propósito/o tipo de informação que ela possui. Para isso, seguem algumas dicas:
|
|
|
|
|
|
|
|
* Evitar abreviações.
|
|
|
|
* Para variáveis booleanas, não nomear apenas com substantivos, para deixar claro que é um booleano. Por exemplo, ao invés de "publish", usem "isPublished".
|
|
|
|
* Criar variáveis fáceis de pronunciar.
|
|
|
|
* Evitar dar nomes muito parecidos para duas variáveis.
|
|
|
|
* Não usar números mágicos. Se existir algum número que precisa ser utilizado no código, criar uma variável para este número para deixar claro o que ele significa.
|
|
|
|
|
|
|
|
### Nomenclatura de métodos
|
|
|
|
|
|
|
|
Variáveis devem ser nomeadas em inglês e seguir o padrão **camelCase**, ou seja, devem iniciar com letra minúscula e cada palavra ou abreviatura no meio da frase deve iniciar com letra maiúscula. Por exemplo:
|
|
|
|
|
|
|
|
* findUserByName :heavy_check_mark:
|
|
|
|
* finduserbyname :x:
|
|
|
|
* find_User_By_Name :x:
|
|
|
|
* FindUserByName :x:
|
|
|
|
|
|
|
|
Além disso, para facilitar a leitura do código, em uma classe os métodos sempre devem estar organizados na ordem abaixo:
|
|
|
|
|
|
|
|
1. Métodos `public`
|
|
|
|
1. Métodos `protected`
|
|
|
|
1. Métodos `private`
|
|
|
|
|
|
|
|
### Padrões das APIs REST
|
|
|
|
|
|
|
|
A API desenvolvida para este projeto deverá seguir o estilo de arquitetura REST, muito utilizado para o desenvolvimento de APIs web, e para isso algumas restrições e boas práticas devem ser seguidos. Neste estilo, as informações gerenciadas pela aplicação e que possuem uma identificação única são chamadas de recursos (pode-se dizer que são mapeamentos conceituais/abstratos às entidades da aplicação), e as URIs da aplicação (*Uniform Resource Identifiers*) devem referenciar estes recursos de forma clara e padronizada.
|
|
|
|
|
|
|
|
Abaixo segue um exemplo de uma API fictícia para ilustrar o padrão que deve ser utilizado pela aplicação:
|
|
|
|
|
|
|
|
* **GET /v1/books** - buscar lista/coleção de livros
|
|
|
|
* **GET /v1/books/1** - buscar um livro específico a partir de um identificador único
|
|
|
|
* **POST /v1/books** - criar/registrar um novo livro
|
|
|
|
* **PATCH /v1/books/1** - editar um livro específico a partir de um identificador único (atualizações parciais)
|
|
|
|
* **PUT /v1/books/1** - editar um livro específico a partir de um identificador único (substituindo a representação do recurso pelos novos dados informados)
|
|
|
|
* **DELETE /v1/books/1** - deletar um livro específico a partir de um identificador único
|
|
|
|
|
|
|
|
Os recursos devem ser referenciados como substantivos no plural, e também deve se adicionar o versionamento da API no começo do *path*, conforme exemplos acima. Além disso, caso exista algum recurso que precise ser representado com mais de uma palavra, estas devem ser separadas por hífens, por exemplo: **/v1/loan-contracts**
|
|
|
|
|
|
|
|
Para mais informações, sugere-se a leitura [deste link](https://www.alura.com.br/artigos/rest-principios-e-boas-praticas), que traz algumas boas práticas de forma didática.
|
|
|
|
|
|
|
|
### Checkstyle
|
|
|
|
|
|
|
|
Para estabelecer padrões e regras de codificação no projeto, e verificar de forma fácil se o código escrito está aderente a estas regras ou não, estabeleceu-se no projeto o uso da ferramenta Checkstyle, que realiza uma análise de código estático e aponta os pontos do código implementado onde ocorreram violações dos padrões. Para este projeto, foi habilitada a verificação das convenções de "Sun's Java Style", que podem ser vistas nesta [documentação](https://checkstyle.org/sun_style.html).
|
|
|
|
|
|
|
|
As instruções de como executar a análise do Checkstyle no projeto podem ser vistas no [README do repositório de Backend](https://tools.ages.pucrs.br/sem-barreiras/sembarreiras-backend/-/blob/main/README.md#checando-padr%C3%B5es-de-codifica%C3%A7%C3%A3o).
|
|
|
|
|
|
|
|
## Tratamento de exceções
|
|
|
|
|
|
|
|
No projeto, dentro do pacote `config`, existe a classe `GlobalExceptionHandler`, que foi criada para que as exceções lançadas pela a aplicação durante a execução sejam tratadas e retornadas com um corpo de resposta e status HTTP de resposta apropriados.
|
|
|
|
|
|
|
|
Em casos de erro, o corpo da resposta deve sempre seguir um formato padronizado, conforme exemplo abaixo. Este formato é aquele modelado pela classe `ErrorResponse`.
|
|
|
|
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
"error": "Mensagem da excecao"
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Para garantir que esse padrão seja seguido para as diferentes exceções que possam ocorrer, foi criado um método genérico para tratamento de exceção nesta classe `GlobalExceptionHandler` onde, ao ser lançada uma exceção do tipo `HttpException`, retorna-se uma resposta com a mensagem e o status HTTP que são atributos desta classe.
|
|
|
|
|
|
|
|
Ou seja, novas exceções criadas dentro do pacote `exception` devem ser subclasses da classe pai `HttpException`, e devem informar no construtor o status HTTP a ser retornado e a mensagem de erro. Assim, será feito o tratamento descrito acima sem precisar de nenhum código adicional.
|
|
|
|
|
|
|
|
Para mais informações, leia [esta documentação do Spring](https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc).
|
|
|
|
|
|
|
|
## Validação de parâmetros das requisições
|
|
|
|
|
|
|
|
Ao receber uma nova requisição, é importante verificar se os parâmetros necessários vieram preenchidos de forma completa e com valores que façam sentido. Uma forma na qual se pode fazer isso é utilizando a API de validação de Beans do Java.
|
|
|
|
|
|
|
|
A API disponibiliza várias anotações que podem ser utilizadas para validar diferentes cenários, a exemplo de:
|
|
|
|
|
|
|
|
* @NotNull: verifica se a variável/atributo é nulo.
|
|
|
|
* @NotBlank: verifica se a variável/atributo String é nulo ou é uma String vazia.
|
|
|
|
* @Min: verifica se a variável/atributo tem valor igual ou maior que o valor mínimo informado.
|
|
|
|
* @Max: verifica se a variável/atributo tem valor igual ou menor que o valor máximo informado.
|
|
|
|
|
|
|
|
Supondo um cenário onde existe um *endpoint* de uma aplicação de biblioteca, para registro de novos livros, onde se recebe um corpo de requisição JSON, para realizar a validação precisariam ser feitos os ajustes abaixo:
|
|
|
|
|
|
|
|
1. Na classe DTO que modela os dados do corpo da requisição, adicionar as anotações nos atributos que se deseja validar.
|
|
|
|
|
|
|
|
```java
|
|
|
|
public class BookRequest {
|
|
|
|
@NotBlank(message = "Titulo precisa ser informado.")
|
|
|
|
private String title;
|
|
|
|
@NotBlank(message = "Autor precisa ser informado.")
|
|
|
|
private String author;
|
|
|
|
@Min(value = 1, message = "Ano de lancamento precisa ser valido.")
|
|
|
|
private Integer releaseYear;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
1. Adicionar a anotação @Valid no método do controller onde se recebe o corpo da requisição. `java @PostMapping public BookResponse createBook(@Valid @RequestBody BookRequest bookRequest) { ... } `Para mais informações, leia [esta página](https://www.baeldung.com/java-validation).
|
|
|
|
|
|
|
|
## Documentação do Swagger
|
|
|
|
|
|
|
|
A ferramenta Swagger (OpenAPI) já foi configurada na aplicação, para documentação da API e para permitir testar requisições HTTP via interface gráfica. Diante disso, novos *controllers* REST que forem criados já vão ser exibidos na [url do Swagger](http://localhost:8080/swagger-ui/index.html) sem precisar de nenhuma configuração adicional.
|
|
|
|
|
|
|
|
No entanto, para garantir que a API esteja bem documentada, sugere-se utilizar as anotações da API do Swagger para descrever melhor quais são os propósitos de cada *endpoint* e quais são os significados de cada parâmetro de requisição e código de resposta. Seguem alguns exemplos de anotação abaixo:
|
|
|
|
|
|
|
|
* @Operation: usada para descrever uma operação no Swagger (adicionar resumo, tags, etc)
|
|
|
|
* @Parameter: usada para descrever um parâmetro de uma requisição HTTP (propósito do parâmetro, exemplo de valor, etc)
|
|
|
|
|
|
|
|
Para mais informações, leia [esta página](https://springdoc.org/v1/). |
|
|
|
\ No newline at end of file |