|
|
| [Home](home) | [**Escopo**](escopo) | [Processo](processo) | [Design/Mockups](design_mockups) | [Configuração](configuracao) | [Arquitetura](arquitetura) | [Gerência](gerencia) | [BD](Banco de Dados) | [Qualidade](qualidade) | [Frontend](frontend) | [Backend](backend)
|
|
|
| :----------: | :-------------------------------: | :------------------: | :--------------: | :--------------------------: | :--------------------: | :------------------------: | :--------------: | :--------------------: | :---------------: | :--------------------: |
|
|
|
| [Home](home) | [**Escopo**](escopo) | [Processo](processo) | [Design/Mockups](design_mockups) | [Configuração](configuracao) | [Arquitetura](arquitetura) | [Gerência](gerencia) | [BD](Banco de Dados) | [Qualidade](qualidade) | [Frontend](frontend) | [Backend](backend) |
|
|
|
| :----------: | :------------------: | :------------------: | :------------------------------: | :--------------------------: | :------------------------: | :------------------: | :------------------: | :--------------------: | :------------------: | :----------------: |
|
|
|
|
|
|
# Backend
|
|
|
|
... | ... | @@ -7,15 +7,38 @@ Esta página centraliza informações sobre o [repositório Backend do projeto S |
|
|
|
|
|
## Sumário
|
|
|
|
|
|
- [Escolha de tecnologias](#escolha-de-tecnologias)
|
|
|
- [Organização do repositório](#organização-do-repositório)
|
|
|
- [Padrões de código](#padrões-de-código)
|
|
|
- [Nomenclatura de classes](#nomenclatura-de-classes)
|
|
|
- [Nomenclatura de variáveis](#nomenclatura-de-variáveis)
|
|
|
- [Nomenclatura de métodos](#nomenclatura-de-métodos)
|
|
|
- [Padrões das APIs REST](#padrões-das-apis-rest)
|
|
|
- [Checkstyle](#checkstyle)
|
|
|
- [Tratamento de exceções](#tratamento-de-exceções)
|
|
|
- [Validação de parâmetros das requisições](#validação-de-parâmetros-das-requisições)
|
|
|
- [Documentação do Swagger](#documentação-do-swagger)
|
|
|
|
|
|
## Escolha de tecnologias
|
|
|
|
|
|
Conforme mencionado na página de configuração de ambiente da Wiki, como tecnologias de Backend foram selecionadas a linguagem Java, junto ao framework Spring Boot, para o desenvolvimento da API, e o banco de dados PostgreSQL.
|
|
|
|
|
|
Esta decisão foi tomada com base em dois momentos. Em um primeiro momento, na primeira semana da Sprint 0, cada integrante citou as tecnologias com as quais tinha experiência e com qual _stack_ tinha mais interesse em trabalhar (Frontend ou Backend) ao se apresentar. Este levantamento demonstrou que, dentre as 16 pessoas AGES 1,2 e 3 do time:
|
|
|
|
|
|
- 9 pessoas tinham experiência com Java (principalmente devido às cadeiras obrigatórias do curso de Engenharia de Software);
|
|
|
- 11 pessoas tinham experiência com JavaScript;
|
|
|
- 4 pessoas tinham experiência com JavaScript e TypeScript;
|
|
|
- 2 pessoas tinham experiência com Python;
|
|
|
- 1 pessoa tinha experiência com Ruby.
|
|
|
|
|
|
Dentre essas 5 alternativas de tecnologia para Backend, a equipe avaliou que faria mais sentido considerar as 3 primeiras (Java, JavaScript, e JavaScript + TypeScript), dado que existiria uma curva de aprendizado menor por grande parte do time já ter utilizado ou visto códigos destas tecnologias anteriormente. Além disso, tanto o Java quanto o JavaScript possuem frameworks conhecidos para o desenvolvimento de APIs Backend (Spring Boot e Node.js com Express), o que implica em muitos materiais didáticos e gratuitos para estudo na Internet.
|
|
|
|
|
|
Diante disso, em um segundo momento, após a apresentação dos _stakeholders_ no dia 08/03/2024, foi realizada uma enquete pelos AGES 3 para que os colegas votassem na tecnologia com a qual tinham interesse em trabalhar, e esta enquete ficou ativa até o dia 11/03/2024. Abaixo, é possível ver os resultados da enquete, de modo que se percebe que as tecnologias mais votadas foram aquelas escolhidas para o projeto:
|
|
|
|
|
|
<img src="./resources/images/resultados-enquete-backend-framework.png" width="450">
|
|
|
|
|
|
<img src="./resources/images/resultados-enquete-backend-db.png" width="450">
|
|
|
|
|
|
## Organização do repositório
|
|
|
|
|
|
O projeto Spring Boot do repositório de Backend do projeto está organizado seguindo um padrão _Controller-Service-Repository_, e esse padrão está refletido na organzação de pacotes do projeto. Optou-se por esse padrão pois, ainda que este padrão possa trazer problemas como acoplamento de regras de negócio e tecnologias de ORM (sendo estes problemas que poderiam ser resolvidos com um padrão de arquitetura limpa), ele segmenta o código em camadas visando uma separação de preocupações/responsabilidades (_Separation of concerns_). Além disso, é um padrão fácil de entender para desenvolvedores que não possuem muita experiência com Spring Boot ou com o desenvolvimento de APIs em geral.
|
... | ... | @@ -23,16 +46,16 @@ O projeto Spring Boot do repositório de Backend do projeto está organizado seg |
|
|
Diante disso, os pacotes do projeto estão divididos da forma abaixo:
|
|
|
|
|
|
- :file_folder: `config`: Configurações personalizadas da aplicação (ex.: configuração da conexão com o banco de dados, declaração de Beans).
|
|
|
- :file_folder: `controller`: Expõe pontos de entrada para comunicação com o mundo exterior. Neste projeto, gerencia a API REST da aplicação, incluindo responsabilidades como autenticação e autorização, e delega o processamento de lógica de negócio para a camada de _services_.
|
|
|
- :file*folder: `controller`: Expõe pontos de entrada para comunicação com o mundo exterior. Neste projeto, gerencia a API REST da aplicação, incluindo responsabilidades como autenticação e autorização, e delega o processamento de lógica de negócio para a camada de \_services*.
|
|
|
- :file_folder: `dto`: Classes utilizadas para transferir dados entre camadas de uma aplicação (não são entidades, são apenas classes que modelam essas informações a serem trafegadas na aplicação).
|
|
|
- :file_folder: `request`: DTOs para dados de entrada das requisições.
|
|
|
- :file_folder: `response`: DTOs para dados de resposta das requisições.
|
|
|
- :file_folder: `exception`: Exceções personalizadas da aplicação.
|
|
|
- :file_folder: `model`: Entidades do negócio.
|
|
|
- :file_folder: `repository`: Encapsula a lógica de acesso ao banco de dados para buscar e persistir dados.
|
|
|
- :file_folder: `impl`: Classes que implementam as interfaces em _repository_.
|
|
|
- :file_folder: `service`: 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: `impl`: Classes que implementam as interfaces em _service_.
|
|
|
- :file*folder: `impl`: Classes que implementam as interfaces em \_repository*.
|
|
|
- :file*folder: `service`: 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: `impl`: Classes que implementam as interfaces em \_service*.
|
|
|
- :file_folder: `util`: Classes e métodos utilitários (ex.: formatação de datas)
|
|
|
|
|
|
## Padrões de código
|
... | ... | @@ -40,12 +63,14 @@ Diante disso, os pacotes do projeto estão divididos da forma abaixo: |
|
|
### 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
|
... | ... | @@ -58,14 +83,16 @@ Além disso, com exceção do pacote model, os nomes das classes devem refletir |
|
|
### 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
|
|
|
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.
|
... | ... | @@ -75,22 +102,49 @@ Além disso, para criar um código limpo e fácil de entender e realizar a manut |
|
|
### 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`
|
|
|
2. Métodos `protected`
|
|
|
3. 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"
|
... | ... | @@ -108,12 +162,14 @@ Para mais informações, leia [esta documentação do Spring](https://spring.io/ |
|
|
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:
|
|
|
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 {
|
... | ... | @@ -126,19 +182,19 @@ Supondo um cenário onde existe um *endpoint* de uma aplicação de biblioteca, |
|
|
}
|
|
|
```
|
|
|
2. Adicionar a anotação @Valid no método do controller onde se recebe o corpo da requisição.
|
|
|
```java
|
|
|
`java
|
|
|
@PostMapping
|
|
|
public BookResponse createBook(@Valid @RequestBody BookRequest bookRequest) {
|
|
|
...
|
|
|
}
|
|
|
```
|
|
|
Para mais informações, leia [esta página](https://www.baeldung.com/java-validation).
|
|
|
`
|
|
|
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.
|
|
|
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:
|
|
|
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)
|
... | ... | |