An open API service indexing awesome lists of open source software.

https://github.com/leoarj/algaworks-esr

ESR - Especialista Spring REST (algafood-api)
https://github.com/leoarj/algaworks-esr

docker docker-compose java redis-cache rest-api spring spring-boot spring-security

Last synced: 3 months ago
JSON representation

ESR - Especialista Spring REST (algafood-api)

Awesome Lists containing this project

README

          

# ESR - Especialista Spring REST (algafood-api)

Projeto MVP de uma API REST para um sistema de *delivery*, baseado no curso ESR, com tópicos avançados de Spring e modelagem de APIs.

![Java](https://img.shields.io/badge/java-%23ED8B00.svg?style=for-the-badge&logo=openjdk&logoColor=white)
![Spring](https://img.shields.io/badge/spring-%236DB33F.svg?style=for-the-badge&logo=spring&logoColor=white)
![Apache Maven](https://img.shields.io/badge/Apache%20Maven-C71A36?style=for-the-badge&logo=Apache%20Maven&logoColor=white)
![Hibernate](https://img.shields.io/badge/Hibernate-59666C?style=for-the-badge&logo=Hibernate&logoColor=white)
![MySQL](https://img.shields.io/badge/mysql-4479A1.svg?style=for-the-badge&logo=mysql&logoColor=white)
![Redis](https://img.shields.io/badge/redis-%23DD0031.svg?style=for-the-badge&logo=redis&logoColor=white)
![Nginx](https://img.shields.io/badge/nginx-%23009639.svg?style=for-the-badge&logo=nginx&logoColor=white)
![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white)
![Swagger](https://img.shields.io/badge/-Swagger-%23Clojure?style=for-the-badge&logo=swagger&logoColor=white)
![AWS](https://img.shields.io/badge/AWS-%23FF9900.svg?style=for-the-badge&logo=amazon-aws&logoColor=white)
![Eclipse](https://img.shields.io/badge/Eclipse-FE7A16.svg?style=for-the-badge&logo=Eclipse&logoColor=white)

## Índice
- [Sobre](#sobre)
- [Modelos EER (Banco de dados)](#modelos-eer-banco-de-dados)
- [Tecnologias e ferramentas utilizadas](#tecnologias-e-ferramentas-utilizadas)
- [Como executar](#como-executar)
- [Pré-requisitos](#pré-requisitos)
- [Executar a partir das imagens do *Docker Hub*](#executar-a-partir-das-imagens-do-docker-hub)
- [Executar a partir de compilação do projeto e construção das imagens *Docker*](#executar-a-partir-de-compilação-do-projeto-e-construção-das-imagens-docker)
- [Testar e acessar a documentação](#testar-e-acessar-a-documentação)
- [Resumo de tópicos explorados](#resumo-de-tópicos-explorados)
- [Autor](#autor)
- [Agradecimentos](#agradecimentos)
- [Licença](#licença)

## Sobre

Projeto MVP de uma API REST para um sistema de *delivery*, baseado no curso ESR, com tópicos avançados de Spring e modelagem de APIs.

### Objetivos
O objetivo do projeto é explorar a modelagem/implementação de APIs REST, utilizando tecnologias do ecossistema Spring, abordando pontos como criação de
*endpoints* **CRUD**, bem como modelagem de operações **não-CRUD**, operações *não convencionais*, e quais as melhores práticas **(ISOs e RFCs)** que podem
ser aplicadas para se implementar as soluções.

#### Sobre o *Deploy*
Foi realizada a *containerização* da aplicação com a *engine* ***Docker***,
em ambiente de **desenvolvimento** com ***Docker Compose*** e em **produção** com componentes de ***Cloud Computing (AWS)***.

O *deploy* em produção foi realizado no modelo ***container serverless***, utilizando o serviço ***AWS Fargate***,
***AWS ECR (Elastic Container Registry)***, ***AWS ECS (Elastic Container Service)***, ***AWS EC2***.

#### Integrações
- Envio de e-mails transacionais
- Foi utilizada o serviço **SES** *(Simple Email Service)* da AWS.
- Armazenamento de arquivos
- Foi utilizado o serviço **S3** *(Simple Storage Service)* da AWS.
- Armazenamento de logs
- Foi utilizado serviço **Loggly** da SolarWinds.

- Não foram implementadas outras integrações, como *gateways* de pagamento, devido didática do projeto.

#### O que não é o foco deste projeto
Não foram abordadas tecnologias de *frontend*, exceto pela adição de simples *clients* da API com *HTML/CSS/JavaScript* e a simples personalização das páginas de autenticação e autorização do *Authorization Server*.

#### Funcionalidades implementadas

O projeto é um *MVP (Mininum Viable Product)*, mas focado no *backend*, onde foram implementadas funcionalidades básicas para o gerenciamento
de entrega de comida, como as listadas abaixo:

##### Recursos e Sub-recursos

- ***Root Entry Point (/v1)***
- ![GET](files/badges/get.svg) - **Listagem dos *endpoints* disponíveis na API**
- ***Host Check (/v1/hostcheck)***
- ![GET](files/badges/get.svg) - **Verificação de integridade com retorno de status `200-OK` com endereço IP do host**
- ***Estados (/v1/estados)***
- ![POST](files/badges/post.svg) - **Criação**
- ![PUT](files/badges/put.svg) - **Atualização**
- ![DELETE](files/badges/delete.svg) - **Remoção**
- ![GET](files/badges/get.svg) - **Busca**
- ![GET](files/badges/get.svg) - **Listagem**
- ***Cidades (/v1/cidades)***
- ![POST](files/badges/post.svg) - **Criação**
- ![PUT](files/badges/put.svg) - **Atualização**
- ![DELETE](files/badges/delete.svg) - **Remoção**
- ![GET](files/badges/get.svg) - **Busca**
- ![GET](files/badges/get.svg) - **Listagem**
- ***Permissões (/v1/permissoes)***
- ![GET](files/badges/get.svg) - **Listagem**
- ***Grupos (/v1/grupos)***
- ![POST](files/badges/post.svg) - **Criação**
- ![PUT](files/badges/put.svg) - **Atualização**
- ![DELETE](files/badges/delete.svg) - **Remoção**
- ![GET](files/badges/get.svg) - **Busca**
- ![GET](files/badges/get.svg) - **Listagem**
- ***Grupos - Permissões (/v1/restaurantes/{grupoId}/permissoes)***
- ![PUT](files/badges/put.svg) - **Associação**
- ![DELETE](files/badges/delete.svg) - **Desassociação**
- ![GET](files/badges/get.svg) - **Listagem com base no ID do grupo**
- ***Usuários (/v1/usuarios)***
- ![POST](files/badges/post.svg) - **Criação**
- ![PUT](files/badges/put.svg) - **Atualização**
- ![PUT](files/badges/put.svg) - **Alteração de senha**
- ![GET](files/badges/get.svg) - **Busca**
- ![GET](files/badges/get.svg) - **Listagem**
- ***Usuários - Grupos (/v1/restaurantes/{usuarioId}/grupos)***
- ![PUT](files/badges/put.svg) - **Associação**
- ![DELETE](files/badges/delete.svg) - **Desassociação**
- ![GET](files/badges/get.svg) - **Listagem com base no ID do usuário**
- ***Cozinhas (/v1/cozinhas)***
- ![POST](files/badges/post.svg) - **Criação**
- ![PUT](files/badges/put.svg) - **Atualização**
- ![DELETE](files/badges/delete.svg) - **Remoção**
- ![GET](files/badges/get.svg) - **Busca**
- ![GET](files/badges/get.svg) - **Listagem**
- ***Formas de pagamento (/v1/formas-pagamento)***
- ![POST](files/badges/post.svg) - **Criação**
- ![PUT](files/badges/put.svg) - **Atualização**
- ![DELETE](files/badges/delete.svg) - **Remoção**
- ![GET](files/badges/get.svg) - **Busca**
- ![GET](files/badges/get.svg) - **Listagem**
- ***Restaurantes (/v1/restaurantes)***
- ![POST](files/badges/post.svg) - **Criação**
- ![PUT](files/badges/put.svg) - **Atualização**
- ![GET](files/badges/get.svg) - **Busca**
- ![GET](files/badges/get.svg) - **Listagem (Todos e por projeção de nome)**
- ![PUT](files/badges/put.svg) - **Ativação (Individual/Múltiplos)**
- ![DELETE](files/badges/delete.svg) - **Desativação (Individual/Múltiplos)**
- ![PUT](files/badges/put.svg) - **Abertura**
- ![DELETE](files/badges/delete.svg) - **Fechamento**
- ***Formas de Pagamento (/v1/restaurantes/{restauranteId}/formas-pagamento)***
- ![PUT](files/badges/put.svg) - **Associação**
- ![DELETE](files/badges/delete.svg) - **Desassociação**
- ![GET](files/badges/get.svg) - **Listagem com base no ID do restaurante**
- ***Usuários responsáveis (/v1/restaurantes/{restauranteId}/responsaveis)***
- ![PUT](files/badges/put.svg) - **Associação**
- ![DELETE](files/badges/delete.svg) - **Desassociação**
- ![GET](files/badges/get.svg) - **Listagem com base no ID do restaurante**
- ***Produtos (/v1/restaurantes/{restauranteId}/produtos)***
- ![POST](files/badges/post.svg) - **Criação**
- ![PUT](files/badges/put.svg) - **Atualização**
- ![GET](files/badges/get.svg) - **Busca**
- ![GET](files/badges/get.svg) - **Listagem com base no ID do restaurante**
- ***Produtos - Fotos (/v1/restaurantes/{restauranteId}/produtos{produtoId}/foto)***
- ![PUT](files/badges/put.svg) - **Atualização**
- ![DELETE](files/badges/delete.svg) - **Remoção**
- ![GET](files/badges/get.svg) - **Busca (representação com link ou download do arquivo)**
- ***Pedidos (/v1/pedidos)***
- ![POST](files/badges/post.svg) - **Criação**
- ![GET](files/badges/get.svg) - **Busca**
- ![GET](files/badges/get.svg) - **Listagem**
- ![PUT](files/badges/put.svg) - **Confirmação**
- ![PUT](files/badges/put.svg) - **Cancelamento**
- ![PUT](files/badges/put.svg) - **Entrega**
- ***OAuth2 - Authorization Server (Autenticação e Autorização)***
- ***Token (/oauth2/token)***
- ![POST](files/badges/post.svg) - **Token via client_credentials**
- ![POST](files/badges/post.svg) - **Token via authorization_code PKCE**
- ![POST](files/badges/post.svg) - **Token via refresh_token**
- ***Authorize (/oauth2/authorize)***
- ![GET](files/badges/get.svg) - **Pedido autorização para obter um token em nome do usuário**
- ***Revoke (oauth2/revoke)*** = *[Pedido de revogação de token]*
- ![POST](files/badges/post.svg) - **Pedido de revogação de token**
- ***Instrospect (/oauth2/introspect)***
- ![POST](files/badges/post.svg) - **Verificação de informações do token**
- ***JWKS (JSON Web Key Set) (oauth2/jwks)***
- ![GET](files/badges/get.svg) - **Informações de chaves criptográficas do servidor (apenas pública)**

## Modelos EER (Banco de dados)
### AlgaFood API

![db-mysql-err-diagram.png](files/db-mysql-err-diagram.png)

- Entidades:
- **estado**
>Tabela para o armazenamento de informações de UFs (em produção seria uma base padronizada (IBGE)).
- **cidade**
>Tabela para o armazenamento de informações de cidades (em produção seria uma base padronizada (IBGE)).

>Se relaciona com **restaurante** e **pedido**, devido o endereço.
- **permissao**
>Tabela para o armazenamento de informações de permissões disponíveis no sistema.
- **grupo**
>Tabela para o armazenamento de informações de grupos de permissões.
- **grupo_permissao**
>Tabela para o armazenamento das associações entre grupos e permissões.
- **usuario**
>Tabela para o armazenamento de informações de usuários.
- **usuario_grupo**
>Tabela para o armazenamento das associações entre usuários e grupos.
- **cozinha**
>Tabela para o armazenamento de informações de cozinhas (tipo de culinária) relacionadas aos restaurantes.
- **forma_pagamento**
>Tabela para o armazenamento de formas de pagamento a serem utilizadas pelos restaurantes.
- **restaurante**
>Tabela para o armazenamento de informações de restaurantes.
- **restaurante_forma_pagamento**
>Tabela para o armazenamento das associações entre restaurantes e formas de pagamento (para quais formas de pagamento estarão disponíveis para cada restaurante).
- **restaurante_usuario_responsavel**
>Tabela para o armazenamento das associações entre restaurantes e usuários (para quais usuários com as devidas permissões poderão gerenciar cada restaurante).
- **produto**
>Tabela para o armazenamento de informações de produtos de restaurantes (cada produto só pode existir relacionado a um restaurante, devido natureza do domínio/negócio).
- **foto_produto**
>Tabela para o armazenamento de informações sobre foto do produto (apenas meta-dados).

>Modelado como 1-1, mas em caso de necessidade, pode ser estendido para que um produto possa ter mais de uma foto.
- **pedido**
>Tabela para o armazenamento de informações de pedidos.
- **item_pedido**
>Tabela para o armazenamento de informações de itens de pedidos.

### *Authorization Server (OAuth2)*

![db-mysql-err-diagram-oauth2.png](files/db-mysql-err-diagram-oauth2.png)

- Entidades:
- **oauth2_registered_client**
>Tabela para o armazenamento de informações de clients da API.

Os clients são outras aplicações *(front-end, back-end)* que acessam a API, diretamente com suas credenciais *(client_credentials)*
e/ou com autorização obtida em nome de um usuário (tokens via *authorization_code*).
- **oauth2_authorization_consent**
>Tabela para o armazenamento de informações referentes às autorizações concedidas (o que o client pode fazer em nome do usuário (escopos)) pelos usuários para determinado client.
- **oauth2_authorization**
>Tabela para o armazenamento de informações referentes às autorizações aprovadas (tokens) para acesso a recursos do sistema, juntamente com *claims* personalizadas.

## Tecnologias e ferramentas utilizadas
- Plataforma: Java 17 LTS (*OpenJDK Temurin*)
- Spring:
- Spring Boot
- DevTools
- Spring Framework
- Spring Web MVC
- Jakarta Bean Validation
- Hibernate Validators
- Spring Mail
- Spring Data
- Spring Data JPA
- Hibernate ORM
- Spring Data Redis
- Spring Security
- OAuth 2.0 Resource Server
- Spring Authorization Server
- Spring HATEOAS
- Suporte a métodos padrão: Lombok
- Versionamento de DB e migrações: Flyway
- Mapeamento DTO: ModelMapper
- Build e empacotamento: Maven
- DB: MySQL Community 8
- IDE: Spring Tool Suite (STS)
- Testes e doc. de API:
- Postman
- Spring Doc (Swagger)
- Versionamento: Git
- Modelo de *braching*: *Git Flow*
- Containerização
- Docker
- Cache distribuído
- Redis
- Armazenamento de logs
- Loggly SolarWinds
- Deploy
- AWS
- AWS Fargate/ECR/ECS/EC2/S3/SES

## Como Executar

### Pré-requisitos
Devido ser uma aplicação containerizada, a forma mais fácil de executar este projeto é utilizando o *Docker*.

- Instalar o Docker para o seu sistema operacional
- https://www.docker.com/products/docker-desktop/
- Dependências
- As dependências para executar a aplicação são baixadas automaticamente (quando via *docker-compose*), porém seguem listadas abaixo em necessidade de uso isolado
- Java: 17 LTS
- DB: MySQL 8.0
- Cache distribuído: Redis 7.4.0
- Proxy reverso: Nginx 1.27.1

- Primeiramente, para executar o projeto com imagens prontas do *Docker Hub* ou compilar o projeto localmente, é necessário:
1. Clonar o repositório
```console
git clone https://github.com/leoarj/algaworks-esr.git
```
2. Acessar o diretório do projeto da API
```console
cd algafood-api
```
3. Configurar variáveis de ambiente
- Variáveis temporárias no *shell* do sistema (a partir de um terminal/cmd)
- MacOS/Linux
```bash
export MYSQL_ROOT_PASSWORD="uma_senha_escolhida"
export SPRINGDOC_SWAGGER_UI_OAUTH_CLIENT_ID="algafood-web"
export SPRINGDOC_SWAGGER_UI_OAUTH_CLIENT_SECRET="web123"
```
- Windows
```bat
set MYSQL_ROOT_PASSWORD="uma_senha_escolhida"
set SPRINGDOC_SWAGGER_UI_OAUTH_CLIENT_ID="algafood-web"
set SPRINGDOC_SWAGGER_UI_OAUTH_CLIENT_SECRET="web123"
```
- Por meio de um arquivo `.env`
- Criar um arquivo `.env` na raíz do projeto com o seguinte conteúdo
```env
MYSQL_ROOT_PASSWORD="uma_senha_escolhida"
```
>Obs.: Para facilidade de execução em ambiente de **testes**.

Em **produção** não é utilizado o usuário ***root***, mas sim um usuário específico, com permissões apenas para *schema* da aplicação.
4. Execução por meio de docker-compose, com proxy reverso, será necessário indicar hosts para o SpringDoc/Swagger
- Variáveis temporárias no *shell* do sistema (a partir de um terminal/cmd)
- MacOS/Linux
```bash
export SPRINGDOC_SWAGGER_UI_BEHIND_PROXY_DEFAULT_HOST="http://127.0.0.1:80"
```
- Windows
```bat
set SPRINGDOC_SWAGGER_UI_BEHIND_PROXY_DEFAULT_HOST="http://127.0.0.1:80"
```
- Por meio de um arquivo `.env`
- Adicionar no mesmo arquivo `.env` na raíz do projeto o seguinte conteúdo
```env
SPRINGDOC_SWAGGER_UI_BEHIND_PROXY_DEFAULT_HOST="http://127.0.0.1:80"
```
>Expondo na porta `80` pois em ambiente de desenvolvimento o Nginx atenderá nessa porta

e os containers do *algafood-api* não estão acessíveis externamente pela porta `8080`.

### Executar a partir das imagens do *Docker Hub*
- Executar via *docker-compose* baixando diretamente as imagens (sem *build* local)
```console
docker compose -f docker-compose-not-build.yml up -d
```
>O comando acima vai baixar as imagens ***algafood-api*** e ***algafood-proxy*** prontas.

- Para executar mais de uma instância do serviço da API, passar o argumento `--scale`
```console
docker compose -f docker-compose-not-build.yml up -d --scale algafood-api=2
```

- Parar execução
```console
docker compose -f docker-compose-not-build.yml down
```

### Executar a partir de compilação do projeto e construção das imagens *Docker*
- Construir o projeto e *build* da imagem
```console
./mvnw package -Pdocker
```
- Executar via *docker-compose*
```console
docker-compose up -d
```
- Parar execução
```console
docker-compose down
```

### Testar e acessar a documentação]
Dependendo da forma que a aplicação foi configurada, a URL para acessar da documentação da API pode mudar.

- Com proxy reverso (Nginx), via docker compose:
```console
http://127.0.0.1/swagger-ui/index.html
```

- Sem proxy reverso (Nginx), ou execução na IDE:
```console
http://127.0.0.1:8080/swagger-ui/index.html
```

## Resumo de tópicos explorados

### APIs
*Application Programming Interface.*

*Conjunto de funções expostas, que intermediam o acesso a um ou vários sistemas.*

- Um resumo desse tópico pode ser visto em:
- https://github.com/leoarj/algaworks-isr#api

### REST = *Representational State Transfer*

Em resumo é uma especificação que define a forma de comunicação entre sistemas na WEB.

- Um resumo desse tópico pode ser visto em:
- https://github.com/leoarj/algaworks-isr#rest--representational-state-transfer

### Spring = Ecossistema

*Conjunto de tecnologias de back-end, onde o objetivo é o desenvolvedor poder focar em regras de negócio e padronizar código de infra-estrutura.*

- Um resumo desse tópico pode ser visto em:
- https://github.com/leoarj/algaworks-isr#spring-rest

- Spring Data JPA:
- https://github.com/leoarj/algaworks-isr#spring-data-jpa

- Spring Security: Projeto e solução do ecossistema Spring para atender a requisitos de segurança em aplicações, como autenticação e autorização.

#### Spring vs Java EE (Jakarta EE)
- **Jakarta EE** é um conjunto de especificações, seu objetivo é padronizar a tecnologia Java para aplicações Enterprise, não é a implementação final.
>JPA por exemplo é uma especificação. E existem as implementações dessa especificação como *Hibernate*, *Eclipse Link*.

>Seu foco é a padronização e retrocompatibilidade.

- **Spring** utilizada especificações do JEE, porém incorpora inovações quem venham a surgir, indepedente do JEE.

- Em alguns momentos competem e em outros colaboram.
>Por exemplo: ***Spring MVC*** utiliza a especificação de ***Servlet***, ***Spring Data JPA*** utiliza a especificação ***JPA***.

#### Injeção de dependências:
- JEE
>CDI
- Spring
>Spring Framework *(IoC Container)*

#### REST APIs:
- JEE
>JAX RS
- Spring
>Spring MVC

### Criando o projeto inicial no *Spring Tool Suite*
#### Passos:
- *Create a project*
- *Spring Starter project*
- Definição:
- Nome
- Gerenciamento de dependências e *build*
- Empacotamento
- Versão Java
- Grupo, artefato, versão, descrição, pacote
- Versão do Spring
- Selecionar o starter *"Web"*

Após isso, é criado um projeto *Maven* e as dependências são baixadas.

### Maven
Ferramenta para gerenciamento de dependências e build da aplicação.

#### Estrutura de diretórios:
- `src.test.java` = classes de teste
- `src.main.java` = código fonte do projeto
- `src.main.resources` = páginas html e outros arquivos de recursos
- raíz = onde fica o `pom.xml`
- `target` = onde ficam os artefatos gerados

#### POM (*Project Object Model*)
Arquivo declarativo de estrutura e dependências do projeto.

**mvnw** = *Maven wrapper* (permite executar o Maven, mesmo não estando instalado).

- Comandos para empacotamento:
```console
./mvnw clean
```
```console
./mvnw package
```

#### Hiearquia de dependências
- Como as dependências se relacionam:
- Dependências **transitivas**
>Dependências com dependências em cadeia.
- Dependências **resolvidas**
>Distinção final das dependências (onde não se repetem mais).

#### Comandos para exibição de dependências
- Exibir árvore de dependências
```console
./mvnw dependency:tree
```
- Exibir dependências resolvidas
```console
./mvnw dependency:resolve
```
- Exibir o pom efetivo (pom final)
```console
./mvnw help:effective-pom
```

#### Repositório local (Pasta **.m2**)
É onde fica o cache de dependências baixadas.

Quando uma depedência for necessária em um projeto e sua versão já estiver disponível no *cache*, o Maven não efetuará novamente o download.

### Criando um primeiro *Controller*
- Controlador WEB
>Componente que recebe e responde requisições HTTP.

- `@Controller`
>Diz que a classe é um controlador Web.
- `@GetMapping("/rota")`
>Diz que o método corresponde a um mapeamento do verbo GET para a rota em questão.
- `@ResponseBody`
>Resposta da requisição.

### Spring Boot DevTools
Permite o *restart* rápido da aplicação após build (em ambiente de desenvolvimento).

#### Para habilitar:
- STS: Botão direito no projeto -> *Spring* -> *Add Devtools*
>É adicionado no `pom.xml` a dependência: `spring-boot-devtools`.

O Spring Boot DevTools consegue fazer isso porque usa 2 *class loaders*:

***Base*** e ***Restart***

- Base
>O que não muda, bibliotecas.
- Restart
>O que se desenvolve e é alterado, código-fonte que passará pelo build (reinicia apenas o classloader de restart).

### Injeção de dependências
É a instanciação e repassagem de uma referência que outro objeto necessita para completar sua tarefa,
sem ter que saber detalhes de como a instância será obtida.

Serve para desacoplar das dependências e é uma forma de *inversão de controle*.

### Inversão de controle (IoC)
Delegar como "executar" para algo externo, não é mais responsabilidade da classe em si
*(passa o controle para um agente externo, apenas recebe o que necessita já resolvido)*.

#### Vantagens:
- Baixo acoplamento.
- Nova implementação não requer grande refatoração.
- Classes bastam apenas conhecer o contrato *(interface)*.
- Facilita testes unitários.

### Spring *IoC Container*
Implementação de injeção de dependência do framework.

Também conhecido como ***Spring Context***.

Alguns conceitos presentes no gerenciamento de instâncias no framework:
- *Bean*
>Objetos gerenciados pelo container (dependency).
- `@ApplicationContext`
>Representa o container de inversão de controle.
- *Bootstrap*
>Inicialização da aplicação. O container escaneia as classes e instancia os beans necessários.
- `@Component`
>Marca uma classe como um Bean (Objeto gerenciado pelo container).
- `@Controller` (Meta anotação)
>Também é um Bean, com semântica de ser um controlador WEB.
- `@SpringBootApplication`
>Todas as classes do pacote e sub-pacotes se tornam elegíveis para serem beans. A partir disso o Spring realiza o *Component Scanning* (no bootstrap).

#### Injetando dependências
##### `@Component`
Para definir beans com instanciação simples.
- Injetado via construtor da classe onde tem referência da dependência.
- Só é possível injetar dependências em uma classe que também for gerenciada pelo container (`@Component`).

##### `@Configuration`
Para a *"Definição de beans"*.
- Para definir beans com instanciação mais complexa (dependência com construtor que recebe argumentos por exemplo).
- É criada uma classe de *Config*, para gerenciar a instanciação dos beans complexos.

###### `@Bean`
- Anotado em método que retorna o bean, na classe de configuração.

###### Observações sobre `@Configuration`
- Quando método definidor de um bean necessitar de outro bean (Na mesma classe de Config)?
>Chamar método de definição do outro bean como **argumento no construtor** do bean que depende dele.

- Quando uma definição de bean depende de outra, mas beans estão em definições diferentes (Classes de Config diferentes) ou o outro bean está anotado como `@Component`?
>Pode ser injetado outro bean a partir de **argumento adicionado no método gerador** e repassando para o bean que depende disso.

#### Pontos de injeção e `@Autowired`

Pontos de injeção são locais onde podemos injetar objetos dentro dos beans.

- Construtor
>`@Autowired` no construtor (qual quer definir para injeção de dependência).

- Setter
>setNomeBean(NomeBean nomeBean) e anotar com `@Autowired`.

- Atributo
>Anotar com `@Autowired`.

##### Boas práticas
- Construtor como ponto de injeção.
- Por questão de clareza, de quais dependências são obrigatórias.
- Facilita os testes, caso queria injetar dependências manualmente.

##### Dependência opcional
- `@Autowired(required = false)`
>Quando não há obrigatoriedade da dependência e a classe consegue trabalhar sem a dependência. Porém caso esteja disponível, será injetada e utilizada.

##### Ideal
- Usar no construtor.
- Usar no atributo.
>Por simplicidade, mas pode dificultar testes.

#### Ambiguidade de beans
Quando há dois ou mais beans para mesmo ponto de injeção e classes que utilizam as dependências.

Dessa forma, se não houver uma meio de desambiguação, não tem como o Spring instanciar e injetar os beans corretamente.

##### Opções para resolução
- Utilizar list do tipo da dependência
>`List beans`.

- Anotação `@Primary` no bean
>Diz qual bean tem precedência para injeção.

>Se anotarmos mais de um candidato com `@Primary`, iremos obter uma `org.springframework.beans.factory.NoUniqueBeanDefinitionException`.

- `@Qualifier("nomebean")`
> Dizer explicitamente qual bean utilizar na dependência e no `@Component` (Checada em *runtime*).

Ao usar, se possível deixar um nome mais abrangente (mais genérico).

- Anotação customizada *(Checada em tempo de compilação) (preferível)*.
- Criar **Enum** e **Anotation**.
>Atributo `value()` é adicionado na anotação, e por padrão com `value` podemos atribuir o valor diretamente na anotação quando formos utilizá-la.

###### Anotações adicionadas na definição da anotação customizada
- `@Rentention`
- `RententionPolicy.RUNTIME`
>Tempo que será mantida (lida).
- `@Qualifier`
>Diz que a anotação customizada é um qualificador também.

### *Profiles*
Forma de separar componentes e comportamento da aplicação conforme diferentes ambientes.

#### Em tempo de desenvolvimento

##### Definindo ambiente dos componentes via anotação
`@Profile("dev")`
>Conforme o ambiente em que a aplicação inicializa, os componentes definidos para outros ambientes não ficam disponíveis.

- A partir do arquivo `application.properties`:
- `src.main.resources`
- `application.properties`
- `spring.profiles.active=prod`

- Via *Spring Tool Suite*:
- *Boot dashboard*
- *Open config*
- *Profile* (informar o profile)

#### Em tempo de execução
Sobreescreve os ambientes definidos no arquivo de propriedades da aplicação.
- Via linha de comando
```console
Java -jar -Dspring.profiles.active=prod projeto.jar
```

#### Detalhes adicionais
É possível passar mais de um *profile*, separando por vírgulas, como:
```properties
spring.profiles.active=prod,postgres
```

### (Utilidades) Comportamentos inconsistentes em tempo de desenvolvimento

#### Aplicação não respeitando o profile do arquivo após várias reinicializações

- 1ª alternativa:
- Executar *clean* e *package* do Maven (preferível).

- 2ª alternativa
- Remover/comentar dependência do *Spring Dev Tools* no `pom.xml`.

#### Erros de build do Maven
Acentos/caracteres especiais nos comentários do `application.properties` podem causar erro ao executar o goal **package** do Maven.

- Remover caracteres especiais que possam estar no arquivo `application.properties` e re-excutar **clean** e **package**.
- Conferir se a codificação do arquivo está em UTF-8.

### Ciclos de vida

São as etapas na existência de um bean (objeto gerenciado pelo container IoC).

- Fases do ciclo de vida de um bean:
- Inicialização.
- Uso (duração).
- Destruição.

#### Métodos de *callback*
São métodos que executam em alguma fase do ciclo de vida.

- Através da anotação de métodos específicos:
```java
// Inicialização (após construção do objeto)
@PostConstruct
init()
```
```java
// Destruição (antes da destruição do objeto)
@PreDestroy
destroy()
```

- Se for através de uma configuração (`@Configuration`):
```java
@Bean(initMethod = "init", destroyMethod = "destroy")
```

- Ou implementar as interfaces `InitializingBean` e `DisposableBean` (Menos utilizado).

### Eventos customizados *(publicando/consumindo)*
- ***EventHandler***
>Ouvinte, capturador de eventos lançados no contexto da aplicação por outro componente.

Eventos customizados no Spring são uma forma de dizer para o container que algum evento ocorreu e disparar isso para "ouvintes".

Dessa forma é possível criar um menor acoplamento e maior abstração, em que ações podem ser executadas com base em um "sinal" emitido por outro componente, mas os componentes (publicadores/consumidores) não precisam se conhecer,
bastando conhecer o contrato do evento, que é uma classe autodescritiva sobre o evento, encapsulando os dados necessários a serem consumidos.

- Criar classe com semântica do evento:
- `ClienteAtivadoEvent`
>Exemplo de classe, para descrever um evento de ativação de um cliente.

Deve ter construtor para injetar objeto para ser consumido (entidade produzida, por exemplo).

- Injetar em uma classe de serviço:
```java
@Autowired
ApplicationEventPublisher
```

- Criar `Listener` *(Vai consumir o event)*:
```java
@Component
NotificacaoService
@EventListener
void clienteAtivadoListener(ClienteAtivado event)
```

### Configurações externalizadas
- Não é boa prática configurações *hard-coded* (dentro do código), mas sim que sejam externalizadas (providas de fora da aplicação).

- Configurações externalizadas evitam comportamentos inesperados da aplicação além de ajudar a prevenir vazamento de dados sensíveis, como senhas e chaves de API.

#### Fontes de configuração
- Através de arquivos de configuração:
- `.properties`
- `.yaml`
- Variáveis de ambiente
- Parâmetros de linha de comando

#### Sobreescrevendo algumas propriedades por linha de comando
- Alteração da porta do servidor:
```console
--server.port=8082
```

- Ou por variável de ambiente:
```console
export SERVER_PORT=8083
```

#### Propriedades customizadas com `@Value`
- Criar a propriedade no arquivo de configuração.
- Criar variáveis na classe que dependem da configuração.

```java
@Value("${nome.propriedade}") // Expression do Spring
Atributo
```

#### `@ConfigurationProperties("prefixo.grupo")` *(Grupo de configurações)*
- `@ConfigurationProperties`
>Marca uma classe como classe de configuração de propriedades.
Utilizando essa anotação é feito o *matching* de **nome-propriedade** para **nomePropriedade**.

#### Configuração do profile conforme ambiente

- Arquivo por ambiente *(development/production)*:
- Criar arquivo com nome do profile a ser utilizado:
- development:
>`application-development.properties`
- production:
>`application-production.properties`

E o profile deve ser configurado no arquivo principal `applicaton.properties` ou provido via linha de comando ou variável de ambiente.

#### Ativando profiles
- Por linha de comando:
```console
--spring.profiles.active=production
```
> Convenção: lower-case separado por ponto.
- Por variável de ambiente:
```console
export SPRING_PROFILES_ACTIVE=production
```
>Convenção: upper-case separado por underscore.

### Persistência de dados

#### JPA e Hibernate

##### ORM (*Object Relational Mapping*)
Mapeamento Objeto Relacional é uma técnica de co-relação de **objetos da aplicação** e **objetos do banco de dados**, onde pode se dizer que uma classe está para uma tabela, assim como um campo da classe está para uma coluna de uma tabela e vice-versa.

Um dos principais objetivos é tornar a aplicação agnóstica do banco de dados, tem um motor (ORM)
que faz a mediação da comunicação da aplicação com um banco de dados específico.

Além disso, manter o paradigma POO e o desacoplamento e independência
de banco de dados, permitindo uma maior evolução da aplicação.

Em Java, implementações de ORM seguem a especificação *Jarkarta Persistence* bem como faz uso em mais baixo nível de **JDBC**.

##### JPA (*Jakarta Persistence API*)
Antigamente também conhecida como *Java Persistence API*.

É um conjunto de especificações que definem como deve ser a implementação de ORMs na plataforma Java,
mas não é a implementação final.

##### Hibernate
Uma das implementações da JPA, sendo assim, é um ORM para a plataforma Java.

##### Starter Spring Data JPA
Starter do Spring para facilitar a habilitação de dependências para prover persistência na aplicação.

- Um resumo desse tópico pode ser visto em:
- https://github.com/leoarj/algaworks-isr#jpa---jakarta-persistence

##### Sobre criação de tabelas a partir do ORM
- Em ambiente de **desenvolvimento**
>Pelo modelo das classes: Permitido, traz produtividade.
- Em ambiente de **produção**
>Não recomendado, o mais indicado é utilizar migrações.

##### Importar dados para testes
- Criar um arquivo para carga de dados a cada start da aplicação:
```console
/src/main/resources/db/testdata/import.sql
```

### Padrão ***Aggregate*** do **DDD** (*Domain Driven Design*)

*"Grupo de objetos de domínio que podem ser agrupados como uma única unidade."*

#### Algumas definições
##### Agregado
- Delimitação que agrupa as unidades.

##### *Aggregate Root*
- Unidade na delimitação que possui maior força.
>"Outras entidades não podem existem sem ela ou são gerenciadas por ela, por exemplo".

##### Exemplos
As entidades **pedido** e **item_pedido** estão na mesma *unidade*, mas o **pedido** é o `<>`.

- Refs.:
- https://martinfowler.com/bliki/DDD_Aggregate.html

### Padrão *Repository*
No padrão *Repository* são criadas classes que encapsulam o acesso a dados
e escondem detalhes de infraestrutura, provendo uma interface para acesso e manipulação de dados.

Por padrão um repositório "imita" uma **coleção**, porém uso mais comum são repositório orientados à **persistência**,
com assinaturas de métodos explícitas orientadas a **CRUD**.

#### Organização nos pacotes
- `domain.repository`
>Interfaces (contratos de persistência)
- `infraestructure.repository`
>Implementações específicas (interação com **ORM**, *queries*, etc)

- Recomendações
- *Não se cria um repositório por tabela/entidade, mas é criado um repositório por **agregado**.*

### REST com Spring
O padrão arquitetural REST baseia-se na tese de doutorado de *Roy Fielding*, e estabelece um modelo de comunicação entre aplicações na WEB.

Com relação à rígigez durante desenvolvimento de APIs REST, os desenvovedores se classificam em dois grupos principais:
- *Puristas*
>API deve seguir estritamente todos os padrões e nível de maturidade propostos.
- *Pragmáticos*
>Podem abrir mão de algumas restrições do padrão, em razão da necessidade de alguma entrega ou análise de *esforço-benefício*.

- Um resumo desse tópico pode ser visto em:
- https://github.com/leoarj/algaworks-isr#rest--representational-state-transfer

#### **HTTP** (*Hyper Text Transfer Protocol*)
HTTP comumente é o protocolo mais utilizado para o modelo REST.

##### Principais Códigos de Status HTTP utilizados nas aplicações
- 100 - Informacional
- 200 - Sucesso
- 200 - Ok
- 201 - Criado
- 204 - Sem conteúdo
- 300 - Redirecionamento
- 301 - Movido permanentemente
- 302 - Encontrado
- 400 - Erro do Cliente
- 400 - Requisição mal-feita
- 401 - Não autorizado
- 403 - Proibido
- 404 - Não encontrado
- 405 - Método não permitido
- 406 - Não aceito
- 500 - Erro do Servidor
- 500 - Erro interno do servidor
- 503 - Serviço indisponível

- Um resumo desse tópico pode ser visto em:
- https://github.com/leoarj/algaworks-isr#protocolo-http

#### Definição de status com `@ResponseStatus`
- `@ResponseStatus(HttpStatus.OK)`
>Marca um endpoint de um controlador para retornar 200 - OK
caso a operação ocorra sem problemas.

>Utilizado quando não se necessita manipular o status de resposta, conteúdo e headers.

- `@ResponseEntity`
>Controle mais fino da resposta (Definição de *headers* e *status* HTTP).

>Classe `HttpHeaders` pode ser utilizada pada definição de cabeçalhos na resposta.

#### Boas práticas (Retorno para *collection* vazia)
- Endpoint existe, retorna dados, mas no caso em específico está sem dados para retornar
>`200 - OK`
- Endpoint não existe
>`404 - NOT FOUND`
- Operação no endpoint realmente não retorna nenhum dado (semânticamente)
> `204 - NO CONTENT`

#### Atualização parcial de recursos com *PATCH*
- Mapeamento para verbo PATCH (atualização parcial de recurso).

- O verbo PATCH é utilizado quando queremos atualizar um recurso parcialmente,
ou seja, não alterando todas as propriedades na requisição.

- Não é uma atualização convencional como no verbo *PUT*, onde transferimos a representação completa do recurso,
mas necessita de um tratamento específico para identificar as propriedades que foram alteradas.

- No caso, pode ser passado um `Map` como argumento no método do enpoint, onde o corpo da requisição será serializado,
e posteriormente aplicado na atualização do recurso.

- *Reflection* pode ser utilizada para conseguir manipular somente as propriedades alteradas.

>Sua implementação é bem específica, e a necessidade deve ser analisada, além de que há outras formas para substituir o uso de PATCH.

### *Domain Services*
Serviços de domínio definem e encapsulam comportamentos relacionados às **regras de negócio**.

#### Boas práticas
- Classes de serviço não tem conhecimento nenhum do protocolo HTTP.
- Podem ser usados para tratar exceções de *infraestrutura*.
>"Evitar tratar tais exceções no controlador".
- "Traduzir" exceção específica em outra exceção que possa ser tratada pelo controlador *(Exceção de negócio)*.

### Modelo de Maturidade de Richardson (RMM)
O *Modelo de Maturidade de Richardson* define restrições para classificar
a maturidade de uma API RESTful, ou seja, o quanto ela atende à especificação do modelo REST.

#### Nível 0: POX (*Plain Old XML*)
- Usa o protocolo HTTP apenas como meio de transporte,
não fazendo uso adequado dos verbos e códigos de status.

- A definição da operação é inclusa no corpo da requisição
e geralmente a URL é única para todas as operações.

Nesse nível, não é considerada uma API REST.

#### Nível 1: Recursos
- Tipo da operação ainda informada no corpo da requisição,
porém nesse caso possui diferentes recursos (Diferentes URIs).

Nesse nível ainda não é considerada uma API REST.

#### Nível 2: Verbos HTTP
- Utiliza corretamente os verbos e status do protocolo HTTP,
além do corpo da requisição/resposta tratar apenas de dados.

- A operação sobre recursos é feita baseando-se na semântica dos verbos HTTP
e a resposta para os clientes é baseada nos códigos de status do HTTP para definir o resultado do processamento.

Nesse nível, partindo-se de uma análise pragmática, uma API já é considerada *RESTful*.

#### Nível 3: *HATEOAS (Hypertext As The Engine of Application State)*.
- Também chamada de *Hypermedia*.

- Nesse nível temos recursos interlingando uns com os outros,
onde dependendo do resultado da solicitação, são recebidos *URIs*
para acessar outros recursos relacionados.

- O objetivo é de ajudar os clientes da API a descobrirem as funcionalidades e o fluxo de navegação.

Nesse nível há o *Root Entry Point* (`/`), que basicamente é o ponto de entrada da aplicação.

E a partir disso, podemos ter recursos que apontam para outros confore o resultado das solicitações,
com por exemplo:

```console
/ -> /produtos -> /produto/{id} -> /fornecedor{id} -> /fornecedores
```

No exemplo acima:
- A partir de uma *collection resource* de **produtos**
temos a possibilidade de acessar um *singleton resouce* de **produto**.

- A partir de um *singleton resource* de **produto** temos a possibilidade de acessar
um *singleton resource* de **fornecedor** associado ao **produto**.

- A partir do *singleton resource* de **fornecedor** temos a possibilidade
de acessar uma *collection resource* de **fornecedores**.

Ou seja, além dos dados retorna os links (URIs) na resposta:

```json
HTTP/1.1 200 OK

{
"id": 73,
"nome": "Macbook Pro 13",
"preco": 15000,
"_links": {
"inativar": "/pagamentos/73",
"fornecedor": "/fornecedores/34"
}
}
```

*Algumas operações só podem ser executadas dependendo do contexto.*

Exemplo: Produto **inativado**, não tem porque informar o link de **inativação** mas sim o de **ativação**.

>Esse nível é conhecido como *"a glória do REST"*, é uma boa prática implementá-lo,
contudo deve-se avaliar **esforço/benefício** na entrega das funcionalidades aos clientes da API.

### JPA com Spring (Spring Data JPA) - Detalhes adicionais

#### Externalização de consultas JPQL para arquivo XML
É possível remover strings de JPQL nas classes transferindo-as para um arquivo XML.

Esse é um dos meios de deixar o código mais limpo caso existam muitas consultas em JPQL.

- Deve se criar um arquivo `orm.xml`
>Arquivo para externalizar querys JPQL.

- Contido na pasta `resources/META-INF`
>Pasta para arquivos de meta-informações.

- No arquivo deve ser descrita a query nomeada
com base no nome da `entidade.metodoDoRepositorio`, por exemplo:
- Método no repositório referente a entidade Restaurante:
```java
// @Query("from Restaurante where nome like %:nome% and cozinha.id = :id")
List consultarPorNome(String nome, @Param("id") Long cozinha);
```
- Código xml da consulta externalizada (consultarPorNome):
```xml



from Restaurante
where nome like concat('%', :nome, '%')
and cozinha.id = :id


```

#### *Criteria API*
*Criteria API* do JPA é uma especificação para se poder definir consultas dinâmicas de forma programática.

Com Spring Data JPA também é possível utilizar essa especificação.

#### Padrão *Especifications* (DDD)
O padrão *Specification* (*Spec*) do **DDD** (*Domain Driven Design*) é um padrão onde se encapsula uma restrição ou regra (Specs),
a fim de abstrair uma função de **predicado** (*teste para dizer se estado atende a determinada condição*).

Spring Data JPA também já implementa esse padrão e é possível utilizarmos ele em conjunto para deixar
as consultas mais dinâmicas, expansíveis, seguras em tempo de compilação e com melhor manutenibilidade.

- `org.springframework.data.jpa.domain.Specification`
>Cada classe que representar uma spec deve implementar essa interface.
- `org.springframework.data.jpa.repository.JpaSpecificationExecutor`
>Cada repositório em que se deseja o suporte
a specifications deve extender essa interface.

##### Referência circular em repositórios (`@Lazy`)
>*"Em alguns casos, para atender a composição de consultas, se faz necessário que a implementação customizada do respositório tenha acesso ao repositório padrão,
tendo que injetar um bean do repositório padrão
na implementação customizada."*

Essa situação causa erro de **referência-circular**, onde o container **IoC** tenta
***instanciar um bean que depende de outro/tem relação, e esse mesmo depende do bean anterior***, causando um *loop* infinito.

Para resolver esse problema, a dependência deve ser anotada com `@Lazy`, dessa forma uma referência só será injetada no momento do uso e quando o bean sem
a dependência *lazy* estiver instanciado.

##### CustomJpaRepository
O Spring Data JPA fornece extensos mecanismos prontos para a maioria das cosultas,
porém também é possível criar repositórios base customizados a fim de termos comportamentos padrão específicos que possam ser herdados por todos os repositórios.

Para atender a essa necessidade, deve-se:
- Criar uma interface chamada `CustomJpaRepository` que herde de `JpaRepository`.
- Criar uma classe chamada `CustomJpaRepositoryImpl` que estenda `SimpleJpaRepository` e implemente `CustomJpaRepository`.
- Nos respositórios estender de `CustomJpaRepository`.
- Por fim, para habilitar o uso de repositórios customizados, habilitar na classe da aplicação com a anotação `@EnableJpaRepositories(repositoryBaseClass = CustomJpaRepositoryImpl.class)`, passando a classe de implementação customizada.

##### Observações adicionais ***(N+1 Problem)***

- `@ManyToOne` = Utiliza estratégia *Eager* por padrão.
- `@OneToMany` = Utiliza estratégia *Lazy* por padrão.

Usar um ORM sem os devidos cuidados, pode ocasionar o "N+1 Problem".

- 1ª Situação: Quando **uma** consulta **inicial** precisa de mais **N** consultas para **cada entidade associada** (mapeadas com `@ManyToOne`)
para trazer os dados das mesmas, de forma que em vez existir uma única consulta com ***JOINS***, existem **N** consultas subsequentes para cada dependência.

Exemplo:

- Recuperar uma coleção de **Pedidos** do banco de dados,
porém a entidade **Pedido** está relacionada com as entidades **Restaurante** e **Usuário (Cliente)**, ou seja 2 dependências.

- Dessa forma haverá:
- 1 consulta para recuperar os pedidos.
- Para cada pedido haverão mais 2 consultas,
1 para trazer informações do restaurante e 1 para trazer informações do cliente.
>**No pior dos casos:** Onde cada pedido possuir restaurante e cliente exclusivamente diferentes.

>**No melhor dos casos:** Considerando o cache do ORM,
que não realiza novas consultas no DB caso o objeto (com base em sua identidade) já esteja em memória,
existe a possibilidade das consultas adicionais para cada entidade relacionada serem reduzidas,
ainda assim, estaremos trabalhando de forma incerta.

- 2ª Situação: Quando buscamos uma coleção de objetos de uma entidade **principal**, mas ela possui um mapeamento
para uma coleção de uma entidade **filha** (mapeado com `@OneToMany`).
>Como `@OneToMany` é ***LAZY*** por padrão, os objetos dependentes não serão carregados na mesma consulta
(o que de fato é performático dependendo do contexto),
mas se posteriormente precisarmos acessar a coleção de objetos dependentes,
será executada uma consulta para recuperar cada coleção dos dependentes de cada entidade principal.

Exemplo:

- Recuperamos uma coleção de **Pedido**, porém essa entidade possui uma coleção de **ItemPedido**, a qual queremos consumir (como imprimir os dados).
>Essa coleção está mapeada com `@OneToMany`.

- Ao consumir os dados como abaixo, teremos o Problema de N+1:
```java
for (Pedido pedido : pedidos) {
List itens = pedido.getItens(); // Aqui dispara consulta
//... outras operações
}
```

- Então se tivermos em uma base de dados `100.000` pedidos, ao recuperar esses pedidos e por ventura resolvermos acessar os itens, haverão:
- **1** consulta para recuperar os pedidos.
- Para cada pedido acessado, pedindo seus itens com um `getItens()`,
mais **1** consulta adicional, ou seja, `100.000 + 1 = 100.001` consultas,
onde **100.000** é o número de pedidos (para recuperar os itens de cada pedido é feita uma consulta)
e **1** é a consulta inicial que trás os pedidos (entidade principal).

- Sendo assim, existem algumas soluções:
- Usar `join fecth` na consulta JPQL.
- Alterar a estratégia da `@ManyToOne` para `FetchType.LAZY` nos mapeamentos para as entidades relacionadas,
no caso de consulta com uma **entidade principal com outras entidades relacionadas**.

>Referente a coleções dependentes, não é uma boa ideia carregá-las sempre que consultarmos a entidade principal,
como no exemplo do **Pedido**, nem sempre precisamos dos dados dos itens, em outras situações podemos precisar.

- Então, uma opção é criar uma consulta separada no repositório do agregado,
**explicitando** o caregamento dos itens na assinatura do método, por exemplo:

```java
//... PedidoRepository

// Quando precisar recuperar pedidos com itens (de uma vez),
// pode por exemplo usar um método específico em vez de findAll()
@Query("from Pedido p join fetch p.itens")
List findAllComItens();
```

- Outro exemplo, a saída de consulta uma SQL gerada pelo ORM com entidade **Restaurante** (mapeando **Cozinha** com `@ManyToOne`):

```sql
-- Com N+1 (Várias consultas adicionais, sem join fetch)
Hibernate:
-- Dados dos restaurantes
select
r1_0.id,
r1_0.aberto,
r1_0.ativo,
r1_0.cozinha_id,
r1_0.data_atualizacao,
r1_0.data_cadastro,
r1_0.endereco_bairro,
r1_0.endereco_cep,
r1_0.endereco_cidade_id,
r1_0.endereco_complemento,
r1_0.endereco_logradouro,
r1_0.endereco_numero,
r1_0.nome,
r1_0.taxa_frete
from
restaurante r1_0
-- Dados das cozinhas
Hibernate:
select
c1_0.id,
c1_0.nome
from
cozinha c1_0
where
c1_0.id=?
Hibernate:
select
c1_0.id,
c1_0.nome
from
cozinha c1_0
where
c1_0.id=?
Hibernate:
select
c1_0.id,
c1_0.nome
from
cozinha c1_0
where
c1_0.id=?
Hibernate:
select
c1_0.id,
c1_0.nome
from
cozinha c1_0
where
c1_0.id=?
```
```sql
-- Sem N+1 (Consulta única com joins)
-- Correção via JPQL = "from Restaurante r join fetch r.cozinha"
Hibernate:
-- Dados dos restaurantes e cozinhas
select
r1_0.id,
r1_0.aberto,
r1_0.ativo,
c1_0.id,
c1_0.nome,
r1_0.data_atualizacao,
r1_0.data_cadastro,
r1_0.endereco_bairro,
r1_0.endereco_cep,
r1_0.endereco_cidade_id,
r1_0.endereco_complemento,
r1_0.endereco_logradouro,
r1_0.endereco_numero,
r1_0.nome,
r1_0.taxa_frete
from
restaurante r1_0
join
cozinha c1_0
on c1_0.id=r1_0.cozinha_id
```

Em fim, essas são uma das situações onde esse problema pode acontecer, mas existem outras formas de ocorrência e solução.

Por isso é importante observar os logs gerados pelo ORM e mitigar isso antes da aplicação ir para produção, pois é uma situação que pode degradar a performance do sistema.

- Refs.:
- https://blog.algaworks.com/o-problema-do-n-mais-um/
- https://www.baeldung.com/cs/orm-n-plus-one-select-problem
- https://vladmihalcea.com/n-plus-1-query-problem/

##### **Pool de conexões**

Um pool de conexões é um mecanismo que fornece um gerenciamento eficiente de conexões de banco de dados
para sistemas, principalmente aqueles precisam trabalhar com conexões simultâneas.

- ***HikariCP***
>Uma implementação de *connection pool* para a camada JDBC, conhecido pela robustez e alto desempenho.

- É possível configurar as propriedades do HikariCP (que vem como uma dependência do starter JPA),
com as seguintes propriedades abaixo:
```properties
# Exemplo de configuracao do pool de conexoes (HikariCP)
# Numero maximo de conexoes a serem criadas caso necessario
# Numero minimo de conexoes iniciais
# Tempo limite para retencao de conexoes excedentes que nao estejam sendo utilizadas
spring.datasource.hikari.maximum-pool-size=5
spring.datasource.hikari.minimum-idle=3
spring.datasource.hikari.idle-timeout=10000
```

- Refs.:
- https://github.com/brettwooldridge/HikariCP

##### **Migrações de DB com Flyway**

*Flyway é um projeto que permite o gerenciamento dos objetos do banco de dados, como o versionamento e manutenção de migrações (Migrations), controlando a execução de scripts.*

- Refs.:
- https://github.com/leoarj/algaworks-isr?tab=readme-ov-file#flyway

- Criando migração:
```md
- resources
- db.migration
- nome arquivo: V00X__descricao-migracao.sql
```

- Erro de *checksum* (Reparação de migrações com erro):
- Deletar linha de histórico.
- Executar reparação pelo Maven:
```console
./mvnw flyway:repair -Dflyway.configFiles=src/main/resources/flyway.properties
```
>Obs.: Somente em ambiente de desenvolvimento.

#### *Exceptions* - Captura e tratamento com `@ControllerAdvice` e **RFC 7807 *(Problem Details for HTTP APIs)***

Neste projeto foi implementada a captura e tratameto de exceptions, de modo a processar os erros de forma correta
*(logging, reversão de transações etc)*, retornar um resposta adequada para os consumidores da API *(Status HTTP e body)*,
bem como evitar expor informações sensíveis/específicas do ambiente *(rastramento de erros)* para aplicações externas.

- Podemos ter captura e tratamento de exceçoes a nível das camadas (infraestructure, service, domain, controller, entre outras),
traduzindo essas exceções em algo mais adequado para a classe `ApiExceptionHandler` apresentar.

```java
@ControllerAdvice // Para capturar globalmente as exceptions dos controllers
public class ApiExceptionHandler extends ResponseEntityExceptionHandler {
//...
}
```

- Um resumo desse tópico pode ser visto em:
- https://github.com/leoarj/algaworks-isr?tab=readme-ov-file#captura-de-erros-do-controlador-com-exceptionhandler

### Validação com *Jakarta Bean Validation*

- Um resumo desse tópico pode ser visto em:
- https://github.com/leoarj/algaworks-isr?tab=readme-ov-file#jarkarta-bean-validation

Além do uso das anotações padrão do *Bean Validation*, foram aplicadas neste projeto
a criação de anotações personalizadas com implementação de `ConstraintValidator` para a validação de propriedades.

#### Exemplo: Anotação para validar *content-type* permitido para upload de foto de produto.

1. Criar a *annotation*:
```java
/*
https://github.com/leoarj/algaworks-esr/files/main/algafood-api/src/main/java/com/algaworks/algafood/core/validation/FileContentType.java
*/

package com.algaworks.algafood.core.validation;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;

/**
* O elemento anotado deve respeitar os formatos de arquivo informados.
* @implNote Chave FileContentType.message para personalizar mensagem de validação no arquivo de mensagens.
* @implNote Expressão ${allowedValues} para interpolar as extensões de arquivos permitidos na mensagem de validação.
* @implSpec Mais detalhes na implementação {@link FileContentTypeValidator}
*/
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Constraint(validatedBy = { FileContentTypeValidator.class })
public @interface FileContentType {

public static final String ALLOWED_CONTENT_TYPES_EXPRESSION_VARIABLE = "allowedValues";

String message() default "{FileContentType.message}";

Class>[] groups() default { };

Class extends Payload>[] payload() default { };

String[] allowed();
}
```

2. Implementar a lógica de validação:
```java
/*
https://github.com/leoarj/algaworks-esr/files/main/algafood-api/src/main/java/com/algaworks/algafood/core/validation/FileContentTypeValidator.java
*/

package com.algaworks.algafood.core.validation;

import java.util.Set;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;
import org.springframework.web.multipart.MultipartFile;

/**
* Implementação para validar elemento anotado com {@link FileContentType}
* @implNote Utiliza customização do contexto de validação para personalizar mensagens.
* @see {@linkplain https://docs.jboss.org/hibernate/validator/6.2/reference/en-US/html_single/#_custom_contexts}
*/
public class FileContentTypeValidator implements
ConstraintValidator {

private Set allowedContentTypes;

@Override
public void initialize(FileContentType constraintAnnotation) {
allowedContentTypes = Set.of(constraintAnnotation.allowed());
}

@Override
public boolean isValid(MultipartFile value, ConstraintValidatorContext context) {
// Retorna instância do provedor de contexto de validação para acesso a APIs específicas (Hibernate Validator)
context
.unwrap(HibernateConstraintValidatorContext.class)
.addExpressionVariable(FileContentType.ALLOWED_CONTENT_TYPES_EXPRESSION_VARIABLE, allowedContentTypes.toString()); //${}
// ou
//.addMessageParameter(ALLOWED_MEDIA_TYPES_EXPRESSION_VARIABLE_NAME, allowedMediaTypes.toString()); //{}

return value == null || allowedContentTypes.contains(value.getContentType());
}
}
```

3. Anotar propriedade no modelo de representação de entrada:
```java
/*
https://github.com/leoarj/algaworks-esr/files/main/algafood-api/src/main/java/com/algaworks/algafood/api/v1/model/input/FotoProdutoInput.java
*/

//...
@FileContentType(allowed = {MediaType.IMAGE_JPEG_VALUE, MediaType.IMAGE_PNG_VALUE})
private MultipartFile arquivo;
//...
```

4. Definir mensagem personalizada, utilizando *expression variable* definida:
```properties
# https://github.com/leoarj/algaworks-esr/files/main/algafood-api/src/main/resources/messages.properties
# ...
FileContentType.message={0} deve ser de um dos tipos ${allowedValues}
# ...
```

#### *Resource bundle* padrão para mensagens de validação

O *resource bundle* específico do *Bean Validation (Hibernate Validator)*
`/org/hibernate/validator/ValidationMessages.properties`
é resolvido primeiro que o resource bundle do Spring.

- Dessa forma, um dos modos de personalizar as mensagens de validação, é criar uma configuração
com um *bean* para que o `messages.properties` se torne o padrão, por exemplo:

```java
/*
* https://github.com/leoarj/algaworks-esr/files/main/algafood-api/src/main/java/com/algaworks/algafood/core/validation/ValidationConfig.java
*/

package com.algaworks.algafood.core.validation;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

/**
* Personaliza a fonte de mensagens para a fábrica de validadores.
*/
@Configuration
public class ValidationConfig {

@Bean
public LocalValidatorFactoryBean validator(MessageSource messageSource) {
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
// resource bundle do Spring (message.properties) se torna o recurso padrão
bean.setValidationMessageSource(messageSource);
return bean;
}
}
```

### Testes de integração e testes de API

*Escrever testes são um importante ponto de apoio para a evolução segura do sistema, onde poderão ser adicionadas novas funcionalidades, refatorações e correções
sem "quebrar" funcionalidades existentes ou gerar alterações dos contratos que possam afetar os clientes da API, tornando essas mudanças transparentes aos mesmos.*

- Neste projeto foram implementados exemplos de testes de integração e API, com mais foco nos testes de API.

#### Diferenças básicas entre testes de integração e testes de API:

- Teste integração
- Testa a integração de todos os componentes (*controllers, services, repositorys...*), como interagem, aguardando respostas e/ou exceções esperadas.
- Teste de API
- Chamadas para endpoints do sistema, verificando retornos de status HTTP e reponse-body esperados.
>Testa se cumpre o contrato de API, sem efeitos colaterais, sendo assim, se houverem mudanças na implementação,
desde que o contrato da API permaneça o mesmo, é entendido que os testes de API não devem falhar.

#### *JUnit*

*JUnit é um framework para testes unitários em Java, permitindo testar classes e métodos de forma isolada,
verificando a execução e retorno de métodos.
Os testes podem ser executados automaticamente durante o ciclo de desenvolvimento,
possibilitando a identificação de erros rapidamente.*

##### Configuração
- É autoconfigurado pelo starter `spring-boot-starter-test`.

##### Algumas anotações
- @Test
>Anota método a ser executado como teste.

- @RunWith
>Para acessar recursos como injeção de dependência, recursos do framework (Um contexto do Spring).

- @SpringBootTest
>Habilita na classe funcionalidades do Spring Boot para testes.

- Refs.:
- https://junit.org/junit4/
- https://junit.org/junit5/

#### AssertJ

*AssertJ é uma biblioteca que permite escrever asserções de forma mais declarativa, utilizando encadeamento de métodos,
com uso de interface fluente, de modo que a escrita das asserções fiquem mais próximas a linguagem natural.
Além disso, o AssertJ fornece um suporte mais estendido para asserções que o fornecido pelo JUnit.*

- Refs.:
- https://assertj.github.io/doc/

#### Conceitos de escopos de testes

##### Ambiente
Em testes de sistemas, temos os seguintes conceitos que fazem parte do ambiente necessário:
- **Cenário**
>O contexto do teste, é relacionado a algum sub-domínio do sistema, como por exemplo gerenciamento de cozinhas.
No contexto estão os **atores (objetos)** relacionados no teste,
os atores podem ser objetos de classes relacionadas as funcionalidades de gerenciamento de determinada parte.
- **Ação**
>Interação entre os objetos que deverá produzir os resultados a serem validados. Como os **atores (objetos)** conversam entre si e quais resposta pedem e geram.
- **Validação**
>Verificação dos resultados obtidos a partir da ação, onde os resultados já são esperados em um determinado estado.

#### Nomes de testes

*Os testes seguem um padrão de nomenclatura, que deve dizer o sentido do teste.*

##### Padrão ***given_When_Then***
- `givenCondicao_WhenCondicao_ThenResultado`
- Onde:
- **dado (given)** uma condição **onde (when)** condição, então deve realizar ação.
- Exemplo:
- `jaExisteCozinhaChinesa_QuandoCadastrarCozinhaChinesa_EntaoDeveFalhar()`

##### Padrão ***shoud_When***
- `shoudResultado_WhenAcao`
- Onde:
- **deve (shoud)** retornar um resultado esperado **quando (when)** executar determinada ação.
- Exemplo:
- `devedeveRetornarStatus400_QuandoCadastrarRestauranteComCozinhaInexistente()`.

#### Testes pelo Maven

*Com o Maven, conseguimos executar de forma integrada os testes, devido a integração com os plugins de testes.*

- Comando para executar testes Unitários/API
```console
./mvnw test
```

*"Testes de integração podem se tornar caros (em termos de re-boot da aplicação para build)..."*

>Uma vez adicionado o *starter* do Spring Boot referente a testes, e criados os testes,
eles sempre serão executados a cada *build* da aplicação.

Nesse sentido, pode se dizer que os testes podem se tornar caros,
devido ao fato de que alguns podem demorar para serem executados, principalmente os de API.

##### Plugin ***Failsafe***

O plugin *Failsafe* do Maven é um plugin para a execução de testes de integração.

Ele é uma opção para configurar testes de integração para não executar a cada build, mas poderem ser executados na fase `verify`.

- Comando para executar testes de integração pelo *Failsafe Plugin*
```console
./mvnw verify
```

- Deve haver o sufixo `IT` no nome das classes de testes, por exemplo:
```java
public class CadastroRestauranteIT {
//...
}
```

- Refs.:
- https://maven.apache.org/surefire/maven-failsafe-plugin/

#### Testes de API

*Um "hit" na api = Chamada de API HTTP*

##### REST-Assured

O ***REST_Assured*** é uma biblioteca para testes automatizados de APIs REST com o protocolo HTTP.

- Ele permite uma linguagem declarativa, em que se pode encadear definições e chamadas para testar um endpoint de uma API,
permitando validar seus retornos (status HTTP) e conteúdo (*request-body, response-body*).

- Com ele, é possível testar os diveros verbos do protocolo HTTP *(GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)*.

- Container (servidor HTTP) de teste
>Anotando a classe de teste com `@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)`,
a própria deve levantar um container com porta aleatória para execução dos testes.

##### Biblioteca ***hamcrest***

A biblioteca ***hamcrest*** é utilizada para abstração de lógica de combinações (***matchers***) e validação do corpo da requisição/resposta.

Nos métodos de testes do REST-Assured, são esperados *matchers* (`org.hamcrest.Matcher`),
a biblioteca fornece esses combinadores para construção dos testes.

- Exemplo, na classe `io.restassured.response.ValidatableResponseOptions`, no método `body` é pedido um *matcher*:
```java
T body(String path, Matcher> matcher, Object... additionalKeyMatcherPairs);
```

- Podemos fornecer um `containsStringIgnoringCase`, por exemplo:
```java
public void deveRetornar201_QuandoCadastrarRestauranteComTaxaFreteGratisComDescricaoObrigatoria() {
given()
.body(jsonRestauranteFreteGratisCorreto)
.contentType(ContentType.JSON)
.accept(ContentType.JSON)
.when()
.post()
.then()
.statusCode(HttpStatus.CREATED.value())
// -> Testa ignorando o case-sensitve, se novo restaurante criado tem determinado texto na descrição
.body("nome", containsStringIgnoringCase(RESTAURANTE_FRETE_GRATIS_DESCRICAO))
.body("taxaFrete", equalTo(BigDecimal.ZERO.floatValue()));
}
```

##### Métodos de *callback*

```java
@BeforeEach
setup()
```

Na classe de teste, anotando um método com `@BeforeEach`, estamos definindo o *callback* para preparar o ambiente de execução de cada teste.

- Nesse método de *callback*, podemos por exemplo:
- Configurar log para caso de falha das requisições/respostas.
- Configurar porta.
- Configurar rotas.
- Preparar base de dados de teste (carga de dados).
- Carregar outros arquivos necessários.
>Existe também a possibilidade de usar callback do *Flyway* para voltar estado do DB a cada teste,
*(porém, além da carga de dados, também a re-criação da estrutura do banco)*.

##### Base de testes separada com `@TestPropertySource`

Uma boa prática é termos um *properties* separado para a execução dos testes,
porque por padrão o contexto irá utilizar nosso `application.properties`,
então é recomendado que algumas configurações como url do banco e dados,
entre outras propriedades, devem ser sobreescritas por um *profile*
diferente, afim de que tenhamos a execução separada do banco de produção e desenvolvimento.

1. Criamos nosso `application-test.properties` em `src/test/resources`:
```properties
# Arquivo de propriedades para usar DB separado nos testes
spring.datasource.url=jdbc:mysql://localhost/algafood_test?createDatabaseIfNotExist=true&serverTimezone=UTC
spring.datasource.username=${DB_USER}
spring.datasource.password=${DB_PASSWORD}
#
# ... demais configurações separadas do ambiente de produção e inclusive desenvolvimento
```

2. Anotamos a classe de testes:
```java
@TestPropertySource("/application-test.properties") // Para utilizar propriedades personalizadas de teste
//...
class CadastroCozinhaIT {
//...
}
```

##### Observações adicionais
- *Procurar testar o que agrega valor, criar testes que ajudam a evoluir o projeto, de modo que não se quebre alguma coisa.*
- *Seguir observado foco no contrato da API (Códigos de status por exemplo).*
- *Seguir observado foco na funcionalidade de um endpoint (X coisa deve ser executada com resultados esperado e conhecidos, por exemplo).*
- *Testar caminhos "infelizes" (exceções de casos de uso), criando roteiros de modo a prevenir comportamentos inesperados
de clients da APIs e de usuários finais.*

- Refs.:
- https://rest-assured.io/
- https://hamcrest.org/

### Boas práticas para APIs

#### Controle do nível de transações (`@Transactional`)
>*Anotar métodos públicos de serviços *(services)* que interagem com banco de dados (com uso de repositórios) com `@Transactional`,
para que uma única transação seja utilizada *(na verdade repassa o escopo da transação para um nível superior)*, pois caso haja mais processamentos em um método
e alguma coisa falhar, tudo possa ser revertido e possa ser evitado estado inconsistente no banco de dados.*

#### *Flush*
O JPA posterga alterações para o banco de dados até que o escopo/verificação de atualizações pendentes mude.

>No caso de quando anotamos os métodos de exclusão nos *services* com `@Transactional`,
como é postergado para uma fila *(para execução posterior)*, pode acontecer de não capturar a exceção no momento que desejamos,
no caso, uma chamada a `repository.flush()` é necessária.

#### Respostas para o consumidor em fluxos não definidos ou previstos
>*Não ignorar uma intenção do consumidor, mas retornar um erro para uma operação
que ele tente realizar mas que não retorna um erro explícito.*

#### Classes *Mixin*
São uma forma de separar anotações relacionadas à serialização e questões voltadas a API das classes de modelo do domínio.

- Sua maior utilidade é podermos **personalizar a serialização/deserialização de classes de bibliotecas que não tenhamos acesso,
ou código-fonte que por motivos técnicos ou de negócio não podemos alterar diretamente**.

Em caso de uso:
- Criar a classe no pacote `api/model`.
- Criar o componente para associação entre a classe de modelo do domínio e a classe de mixin na camada da API
`core/jackson/JacksonMixinModule.java`.
- Remover as anotações relacionadas a serialização da classe de domíno do modelo/bibliotexa, pois a serialização/deserialização
será pela classe mixin.

#### ISO 8601 *(Boas práticas com Data e Hora)*

- Usar **ISO 8601** para formatar data/hora.
- Aceite qualquer fuso horário (como entrada e converter sem problemas).
- Armazenar data/hora em UTC (para pode calcular o deslocamento de tempo referente ao UTC).
- Retorne em UTC (Para que o consumidor da API consiga representá-la como desejar em seu fuso horário local).
- Não incluir horário se não necessário (Pode acabar mudando a data final sem necessidade).

- Mais detalhes em:
- https://github.com/leoarj/algaworks-isr?tab=readme-ov-file#iso-8601-boas-pr%C3%A1ticas-com-data-e-hora

- https://www.iso.org/iso-8601-date-and-time-format.html

#### Padrão DTO *(Data Transfer Object)*

*"Nem sempre o que devolvemos como resposta pode ser aceito como entrada de dados em uma API..."*

>O padrão **DTO** é utilizado para separação do modelo de domínio (camada *domain*) para o modelo de representação (*api.model*).
Com ele, criamos classes específicas para transferência de dados (serialização/deserialização), representação de projeções do banco de dados etc.

- Não é uma boa prática usar as entidades diremente para exposição, pois:
- Podermos expor informações desnecessariamente.
- Possibilidade de trabalhar indevidamente com uma entidade que está anexada no contexto de persistência do JPA.

- Detalhes adicionais em:
- https://github.com/leoarj/algaworks-isr?tab=readme-ov-file#resource-representation-model
- https://martinfowler.com/eaaCatalog/dataTransferObject.html

##### ModelMapper

Biblioteca para automatizar o mapeamento do *domain model* para o *representation model* e vice-versa.

- Refs.:
- https://github.com/leoarj/algaworks-isr?tab=readme-ov-file#modelmapper
- https://modelmapper.org/
- https://mapstruct.org/

### Modelagem avançada e implementação da API

Na modelagem e implementação de API, temos alguns conceitos que ditam como ficará o seu desenho final.

#### Modelamento de sub-recursos para relacionamentos
- Granularidade **grossa (baixa)**
> Um *collecion resource* ou um *singleton resource* tem poucos sub-recursos para operações específicas,
e geralmente essas outras operações são mais genéricas, tendo poucos endpoints (daí o nome).
- Granularidade **fina (alta)**
> Um *collecion resource* ou um *singleton resource* tem muitps sub-recursos para operações específicas,
e geralmente essas outras operações são mais especialistas, tendo muitos endpoints (daí o nome), para pequenas operações de preferência.

- Sub-recurso de singleton resource
>`/restaurantes/1/endereco`

- Sub-recurso de collection resource
>`/restaurantes/1/produtos`
>`/restaurantes/1/produtos/100`

##### Chatty vs Chunky

- ***Chatty*** = Granularidade fina.
>Mais de uma chamada para operações comuns.

- ***Chunky*** = Granularidade grossa.
>Operações feitas em uma única requisição

- Para escolher o modelo de granularidade deve-se:
- Pensar nos consumidores da API (quais tipos de clients e o que necessitam).
- Tomar cuidado com inconsistência (endoints repetidos para mesma operação ou que poderiam ser eliminados, visando simplicidade).

- ***Trade-offs***
- Se houver risco de inconsistência, então escolher *chunky*.
- Se precisa executar coisas específicas, desde que não deixe o estado inconsistente, então escolher *chatty*.

#### Conceitos abstratos de negócio e ações não-CRUD

- Conceitos abstratos de negócio e ações **não-CRUD**, são requisitos de negócio que não se encaixam no padrão de manipulação de registros diretamente,
como **criação, leitura, atualização e deleção**, o famoso **CRUD**.
>É importante salientar que sim, são executadas operações de persistência, porém dependem de determinadas condições e um fluxo estabelecido,
de forma que um registro no banco de dados pode ser criado ou atualizado por exemplo,
dado uma ação que pode não ser primariamente a de criação ou atualização do registro.*

##### Exemplos

- Ativação/inativação
- No *payload*
>Não tão recomendado, pode se perder a semântica de ser uma API RESTful.
- Com **PATH**
>Atualização parcial de recursos, implementação pode ficar mais complexa do lado do servidor.
- Com **PUT**
>Endpoints separados (`/ativacao` e/ou `/inativação`) de algum recurso.
- Com **PUT** + *payload*
>Endpoint única passando *true/false* no corpo da requisição por exemplo.
- Com PUT e DELETE
>Recurso único `/ativacao`, mudando a ação conforme o verbo.

#### Boas práticas
- Nome do processo de negócio = Nomear o conceito.
>Com base no mundo real e
se possível ser mais abrangente, e mantendo a regra de substantivo e não verbo,
onde pode ser mantido no plural ou singular, dependendo do contexto, por exemplo:

- `/alteracoes-status`
>Dá a ideia de que podem ser feitas operações referentes a status de algum recurso ou coleção de recursos.
- `/compras/1/pagamentos`
>Dá a ideia de que podem ser feitas operações referentes às formas de pagamento de alguma compra, sendo uma operação com sub-recurso (mais indicado).
- `/notificacoes-restaurantes`
>Dá a ideia de que podem ser feitas operações referentes a notificações em lote para um grupo de restaurantes, processado via *request-body*.

>Criar quando apenas necessário e fizer sentido, para não causar confusão ou ambiguidade para os consumidores da API.

### Um pouco mais sobre JPA
- *Objeto alterado fora da transação é sincronizado com o banco de dados
por causa da anotação `@Transactional` (método transacionado).*
- *Um objeto obtido do contexto de persistência pode ser alterado e sincronizado no DB, mesmo esse objeto estando fora do escopo do método.*
- *`@Transactional` aplica um ***"start/commit transaction"***.*
>É feito *rollback* caso aconteça uma exceção no método transacionado.

#### `detach()` do `EntityManager`
*Útil para tirar um objeto do gerenciamento do JPA,
caso tenha que implementar alguma lógica mais específica em que o objeto a ser
comparado com alguma busca já possa ter sido alterado antes,
causando um erro ao buscar um dado que deveria ser único em uma consulta.*

#### Erro do Lombok com JPA
- `Lombok annotation handler class lombok.eclipse.handlers.HandleEqualsAndHashCode failed`

- Verificar a versão instalada no STS e a versão provida pelo Spring Boot.
- Reinstalar o Lombok.

#### Otimização de consultas
- *Collection resource* (Listagem)
>Resumir dados, apresentar apenas os necessários (DTO resumido e consulta no repository otimizada), de forma a otimizar a serialização e requisições no DB.

Observar também se existem projeções que podem ser passadas pelo endpoint e também processar isso para aplicar dinamicamente nas querys.
- *Singleton resource* (Busca)
>Para obter mais detalhes de um recurso, a exposição de dados adicionais fica bem implementada na sua busca individual.

#### ID vs UUID na URI de recursos
- *É recomendado utilizar UUIDs na identificação da URI de recursos
para não expormos o ID interno sequencial do sistema, de forma a proteger
a exposição de dados que não devem ser acessados indevidamente.*
>O ID interno continua sendo utilizado na identificação dos registros e na integridade do banco de dados,
mas para busca e exposição do modelo de representação é retornado o UUID referente ao recurso.

#### `@PrePersist`
- Anotação `@PrePersist` do Hibernate executa ações na entidade antes de persistir os dados (conforme o ciclo de vida).
>Por exemplo, dados que não podem ser nulos na base de dados podem ser tratados antes com esse *callback*.

- Geração de UUIDs e outros códigos randômicos, quando controlado a **nível da aplicação**, também podem ser gerados, anotando métodos com essa anotação.

- Refs.:
- https://hibernate.org/

### Modelagem de projeções, pesquisas e relatórios

#### Projeção de recursos com *JasonView* (`@JsonView`):
JasonView é ama alternativa para representação de recursos, onde se cria uma *view* (interface)
e dentro dela outras interfaces que representam a projeção daquele recurso.

- No **modelo de representação** (`api/model`) devem ser anotadas as propriedades que
desejamos que façam parte de determinada projeção (resumos por exemplo).

- No **controlador**, anotamos o método correpondente com a mesma projeção,
ou uma outra opção é utilizar o *wrapper* e pegar a projeção de um parâmetro da requisição
e com isso, programaticamente definir a projeção de forma dinâmica.

#### DTO vs JsonView
- Com DTO temos uma flexibilidade maior, além de configurações personalizadas na *config* do *ModelMapper*.
Única questão é que temos que criar DTOs diferentes para outras representações.

- Com JsonView temos um só *model*, e nele associamos as propriedades com as respectivas projeções.
Pode fornecer facilidade ao definir métodos no controlador, porém trás uma poluição maior no código,
além de mais acoplamento, já que as classes de outros models devem também ter as mesmas anotações do model principal.

- A aplicação do JsonView depende de uma análise das necessidades.

- O padrão DTO torna-se o preferível.

#### Filtro de campos retornados pela API com `@JsonFilter`
Usar `@JsonFilter` é uma forma de ter um controle fino de quais campos serão retornados pela API.

Lembrando que esse controle é repassado ao cliente da API, onde ele pode escolher na requisição o que quer receber como retorno,
como clients móveis e/ou limitação de hardware e rede.

Existem discussões se é uma abordagem válida ou não.

No caso, é uma alternativa, podendo ser implementada em casos muito específicos de necessidade.

### Modelagem de pesquisas complexas
Existem algumas formas de se aplicar consultas complexas (filtros e projeções), seguindo algumas observações:
- Passando parâmetros na URL (*query-params*).
- Passando como recurso (`/pedidos/filtro`) com **POST**.
>Onde no corpo da requisição vão ter os filtros e como resposta um **200 - OK** com o resultado do filtro
(não recomendado, pois quebra a constraint de *cache*).
- Considerar a própria pesquisa como um recurso (criar e retornar 201).
- Filtro cadastrado é utilizado em pesquisa (`/pedidos/filtro/234`) com **GET**, que retorna os resultados.
- Passagem do ID do filtro criado como parâmetro na requisição (**GET** `/pedidos?filtro=234`).
>Quando os filtros são realmente complexos (Com condições, critérios e operadores ou utilizam alguma linguagem específica de domínio).

#### Parâmetros opcionais de pesquisa (**DTO** e *Specification*)
Uma forma de usar filtros opcionais, é com um *filter DTO* e processar em uma *specification*.

- Criar DTO `NomeModeloFilter`.
- Criar uma *Spec (Specification)*, que é a classe que contém as lógicas de construção da query, geralmente utilizado *Criteria API*.
- Estender o repository herdar `org.springframework.data.jpa.repository.JpaSpecificationExecutor`.
- Adicionar *Filter DTO* e chamada da Spec (via `service` ou `repository`) no controlador.
>Spring já trata o DTO de filtro automaticamente, caso os parâmetros (nomes) coincidam com os atributos.

- Refs.:
- https://jakarta.ee/learn/docs/jakartaee-tutorial/current/persist/persistence-criteria/persistence-criteria.html

### Paginação e ordenação de resultados da API
Para implementar paginação em um endpoint, deve ser adicionado um `Pageable`
como parâmetro do método e repassar esse `Pageable` para o método `findAll` do `repository`,
de modo que já será feita a paginação a nível de banco de dados.

- Para paginação, os parâmetros na URL devem ser:
- **size**
>Tamanho da página.
- **page**
>Posição da página (começando de zero).

- Para ordenação:
- **sort** = `campo,ordenacao` (opcional)
>É possível fazer o **sort** por mais de um campo.

#### Observações:
- *Na paginação é uma boa prática retornar as informações de página.*

- *No método do controlador que responde pelo **GET** de listagem, deve ser retornado um `Page`.*
>A conversão da lista deve ser realizada, colocando a lista de resultados dentro de um `Page`.

- *Tamanho padrão de página é 20.*
>É possível específicar o tamanho com `@PageableDefault(size = 10)`.

#### Customização da representação de paginação (`JsonSerializer`)
Podemos resumir a representação de meta-dados da paginação, afim de que o retorno não seja poluído ou contenha informações duplicadas.

- Criar classe no pacote `core.jackson`.
- Anotar com `@JsonComponent`.
- Estender de `JsonSerializer`.
- Implementar método `serialize`.

#### Endpoints de consultas com dados agregados *(para gráficos e dashboards)*
*Recursos estatísticos (com agregação), onde o recurso mediante um filtro ou não, vai retornar
dados agregados de várias entidades e totalizadores por exemplo.*

- Algumas alternativas de URIs:
- `/restaurantes/1/estatisticas/vendas-diarias`
- `/pedidos/estatisticas/vendas-diarias`
- `/relatorios/vendas-diarias`
- `/insights/vendas-diarias`

- Ao se implementar as consultas *(queries)* para as estatísticas, uma boa prática é criar uma
*interface* no pacote `domain.service`, e por fim colocar a implementação em uma classe no pacote de **infra-estrutura**.
>O motivo disso é de que como a implementação pode possuir muito código específico, **relacionado ao núcleo de acesso a dados (JPA/Hibernate)**, o ideal é separar essa implementação, para possíveis mudanças e evoluções do código,
além de separar código de **infra-estutura** do código de **negócio**.

- Implementações feitas neste projeto:
- Endpoint com a transformação dos dados consultados, com cálculos e representação no formato `JSON`.
- Endpoint de emissão de relatório em *PDF* com os mesmos dados de projeção da consulta que retorna `JSON`.
>Dessa forma, basta o *client* passar o `Accept` (`application/json` ou `application/pdf`) que deseja na requisição, para o mesmo endpoint.

### *Upload* e *download* de arquivos

*Existem algumas opções para upload de arquivos para um servidor.*

#### Upload de *payload JSON* com arquivo encodado em **Base64**.
- Não muito preferível, em alguns casos arquivo pode ficar 30% maior.
- Dependendo do fluxo de dados pode consumir muita memória do *container (servidor Web)*.
>É uma alternativa, porém não é a forma nativa para transferência de arquivos, porém pode ser usada para fluxos pequenos de dados.

#### Opção nativa do protoloco HTTP (`multipart/form-data`)

```http
PUT /restaurantes/1/produtos/10/foto
Content-Type: multipart/form-data; boundary=XXX

--XXX
Content-Disposition: form-data; name="arquivo"; filename="Prime Rib.jpg"
Content-Type: image/jpeg
dados...
--XXX
Content-Disposition: form-data; name="descricao"

Prime Rib ao ponto
--XXX--
```

No bloco acima, podemos ver a requisição **PUT**, com a opção de `Content-Type: multipart/form-data`

onde a chave `boundary=XXX` representa o delimitador, que separa as múltiplas partes dos dados da requisição.

##### Vantagens
- Como é uma forma nativa do **HTTP**, geralmente maior parte dos servidores (containers Web)
estão preparados para esse fluxo, não ocupando a memória do servidor,
geralmente utilizando uma implementação onde um arquivo temporário é criado.
- Não aumenta o tamanho do arquivo.
- Menor custo de processamento sem o processo de encodar/decodar em base64.

##### Desvantagens
- Para o consumidor da API pode se tornar um pouco mais verboso montar a requisição.

#### Abstração serviço de armazenamento (`StorageService`)
Neste projeto, o service relacionado a armazenamento de arquivos foi abstraído para `Local` e `S3` (`pacote domain.service` e `infrastructure`),
de forma que se possa escolhar a imprementação *(útil em ambientes de desenvolvimento e produção)*.

- Exemplo de dependência do SDK declarada no `pom.xml`:
```xml

com.amazonaws
aws-java-sdk-s3
${aws-java-sdk-s3.version}

```

##### Criação de *bucket* no AWS S3
- Criar bucket no S3, com pasta `catalogo`.
- Criar usuário para acesso ao S3.
- Definir política de acesso ao S3 com:
- Gravação: ***put objetc***, ***delete object***.
- Gerenciamento de permissões: ***PutObjectAcl***, ***PutObjectVersionAcl***.
- Criar **chave** para acesso a API do S3.

##### Definição das ACLs
- Importante definir as *ACLs* como citado acima, para evitar erros como o abaixo:
```console
com.amazonaws.services.s3.model.AmazonS3Exception: The bucket does not allow ACLs (Service: Amazon S3; Status Code: 400; Error Code: AccessControlListNotSupported; ...
```
#### Resumo
Neste projeto foi:
- Utilizado o SDK da Amazon para acesso ao serviço S3.
- Implementada configuração para abstrair e escolher tipo de armazenamento.
- Implementado endpoint para servir arquivos:
- **Localmente**, retornando o *stream* de dados.
- No caso de armazenamento por **terceiros (S3)**, retornando a **URL** do arquivo.
>Não devemos atuar como "ponte" provendo o arquivo, pois que o consumidor da API pode
acessar o arquivo diretamente do S3 com mais performance *(CDN, geolocalizaçao)*, deixando nossa aplicação livre.

- Refs.:
- https://aws.amazon.com/pt/s3/?nc2=type_a
- https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/examples-s3.html

### Emails transacionais e *Domain Events*
Para este projeto, foi aplicada a ideia de envio de e-mails quando acontece algum evento na aplicação,
no caso, transações referentes a **pedidos** de **restaurantes**.

- Para isso é necessário **um serviço SMTP**, e a serviço escohido foi o *Amazon Simple Email Service (SES)*,
utilizando o suporte a *Java Mail* facilitado pelo Spring.

#### *Apache FreeMarker*
Utilizados templates para email com *Apache FreeMarker*,
uma *template engine* para geração dinâmica de arquivos HTML, e-mails entre outros,
por meio de substituição de dados via linguagem de expressão.

- Os arquivos de templates ficam no diretório:
`/resources/templates`

- Refs.:
- https://freemarker.apache.org/

#### Implementações de *services* de e-mail
Foram implementados os seguintes tipos de envio:
- **Smtp** (Produção - envio via SMTP - Na AWS com o SES)
- **Fake** (Desenvolvimento - log console)
- **Sandbox** (Desenvolvimento - envio via SMPT para destinatário fixo)
>No pacote `com.algaworks.algafood.infrastructure.service.email`.

#### Padrão *Domain Events* do **DDD**

Com o padrão de *Domain Events*, um componente da aplicação deve apenas *"dizer"* que um evento do domínio ocorreu,
e outras partes do sistema que ficam ouvindo os eventos *"reagem"* a esses eventos,
executando suas resposabilidades específicas.

>Com Domain Events, temos a aplicação de princípios do **SOLID** como **SRP** e *aberto para extensão, fechado para modificação (Open-closed Principle)*.

##### Vantangens
- Extensibilidade do código, sem ter que alterar o componente original.
>Sendo assim, caso algum componente seja responsável por uma tarefa, ele apenas deve dizer que sua tarefa foi concluída,
não tendo mais nenhuma responsabilidade além dessa, tendo que conhecer outras partes.
Com isso deixamos cada parte com responsabilidade única, em vez de colocar várias responsabilidades e processos espalhados em várias partes.

##### Boas práticas
- Deve-se publicar eventos apenas a partir do ***agregate root***.
- Nomear evento no passado *(como algo que aconteceu)*.

##### Anotação `@TransactionalEventListener`
- Para controlar a fase em que os eventos serão executados (por padrão é antes do *commit* da transação).
>Deve ser pensando em que cenário será usada, para escolher se o evento pode falhar ou não, caso a transação seja concluída.

>No caso do e-mail, o evento pode falhar, mesmo que a transação seja concluída com sucesso,
porém uma exceção de email não pode impedir a transação, por isso é passado para executar após o *commit*.

### **CORS** e consumo da API com JavaScript e Java

*Protocolo + domínio + porta = Origem*

#### Testes
- Iniciar um servidor Web HTTP comum, usando do Python por exemplo:
```console
python3 -m http.server 8000
```
- Erro ao testar URL pelo navegador:
```console
Access to XMLHttpRequest at 'http://api.algafood.local:8080/restaurantes' from origin 'http://www.algafood.local:8000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
```

### *Same Origin Policy* (Política de Mesma Origem)
Uma política de segurança que impede que uma página ou script acesse recursos
de outra origem, de forma a previnir que scripts maliciosos acessem dados de diferentes origens
(que não sejam da mesma origem do documento/script), para que uma origem acesse outra,
é necessário habilitar e utilizar configurações do **CORS**, que é o mecanismo que resolve essa questão de forma padronizada.

#### ***CORS - Cross-Origin Resource Sharing*** (Compartilhamento de recursos entre origens)
- É o mecanismo que define se o acesso de uma origem para outra **(origens cruzadas)** pode ser realizado.
>A configuração é realizada no servidor e este informa por meio de um *header* na resposta para o navegador
se o mesmo precisa ou não bloquear o acesso.

#### `@CrossOrigin`
- Anotação do Spring para configurar o **CORS** em um único endpoint ou em todo o controlador.
>Quando adicionado, a implementação do Spring vai além, retornando status **403**, validando e nem permitando a execução
da requisição.

O *browser* envia na requisição um cabeçalho ***origin***, e o servidor verifica esse cabeçalho e já faz a validação da requisição, bloqueando e retornando **403**.

#### *Preflight* do CORS
Pode ser considerada uma **"pré-requisição"** que o *browser* realiza ao servidor
utilizando o verbo **OPTIONS**, para verificar se pode ou não realizar a posterior chamada de fato.

- O *preflight* é executado sempre que uma requisição não for considerada **"simples"**.
>Requisições simples são definidas com verbos **GET, HEAD ou POST**, e os cabeçalhos da requisição
devem ser de um grupo estabelecido ***(Accept, Accept-Language, Content-Language, Content-Type, Range)***.

Caso a requisição não respeite um desses critérios ela passa a ser condiderada uma requisão **não-simples**,
mesmo que utilize os verbos **GET, HEAD ou POST**.

Para esses testes, foi implementado o consumo da API com simples clients *JavaScript* e *Java* com *RestTemplate*.

- Refs.:
- https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests

### Cache de HTTP
*Basicamente, o conceito de cache se refere a formas de acessar de forma eficiente dados que são
consultados frequentemente, de forma a verificar a validade e integridade
desses dados, com o objetivo de reduzir chamadas diretas ao *Origin Server (Servidor de Origem)*,
onde os *clients* e/ou servidores intermediários (proxys reversos por exemplo) podem manter
uma representação do recurso original por determinado tempo, antes de fato requisitar
uma representação atualizada do recurso.*

#### *Origin Server*
*O Origin Server (Servidor de Origem) é o servidor onde as informações são servidas e armazenadas de forma definitiva.

No conceito de cache HTTP, a ideia é que o Origin Server seja somente requisitado quando realmente necessário,
no que diz respeito a informações solicitadas com muita frequência.*

#### Tipos de cache

##### Cache local (privado)
*O cache local ou privado é o cache mantido por um client da API (browser, aplicativo móvel etc), um armazenamento local (em disco ou memória por exemplo),
onde ele mantém uma representação do recurso original recuperado a partir do Origin Server ou *cache compartilhado*.

Essa representação local deve ter algum mecanismo (tempo de vida do cache) para verificar a validade, de modo que o client possa reutilizá-la,
antes de requisitar uma nova representação.*

- Tipos de representação:
- Representação ***fresh***
>Representação "fresca" - Dados estão dentro de um parâmetro de validade e podem ser reutilizados.
- Representação ***stale***
>Representação "velha" - Dados estão fora de um parâmetro de validade e devem ser requisitados da origem novamente.

##### Cache compartilhado
*O cache compartilhado é o cache mantido por um servidor intermediário *(proxy reverso, CDN)*,
onde o mesmo faz a intermediação entre os clients e o Origin Server,
de modo que ele fornece as atualizadas quando dentro dos parâmetros, e quando seu cache estiver invalidado, requisita ao Origin Server ou redireciona
a requisição do client diretamente para o Origin Server.*
- Exemplos:
- *NGINX*
- *CloudFlare CDN*
>No conceito de proxy HTTP, dizemos que os caches compartilhados/intermediários ficam *"na frente"* do Origin Server.

- Refs.:
- https://nginx.org/en/
- https://www.cloudflare.com/application-services/products/cdn/

#### Benefícios
- Reduz uso de banda.
- Reduz lantência.
- Reduz carga nos servidores.
- Esconde problemas de rede.

>O cache HTTP já utiliza infra-estrutura e especificações da Web,
onde servidores e clients seguem ou devem seguir essas especificações, criando mecanismos que atendam a esses requisitos.

#### Quando não usar
- Quando clients não toleram diferenças na representação do estado (não deve haver incerteza).
- Quando os dados mudam com muita frequência.

#### *Talend API*
Pode ser utilizada a ferramenta *Talend API tester* para testar a API utilizando os recursos de cache do navegador e observar os resultados.

https://www.talend.com/products/application-integration/cloud-api-services/

#### ETags *(Entity Tags)*
*ETags ou Entity Tags são identificadores baseados em um hash da representação (versão do recurso), o qual é calculado pelo Origin Server e fornecido como um
header na resposta da requisição.*

##### Validações e requisições condicionais
- *ETags permitem validações e requisições condicionais.*
- Os clients utilizam esse idenficador nas próximas requisições, adicionando o cabeçalho `If-None-Match`,
o qual o servidor irá comparar e decidir se envia novamente o conteúdo ou responde com status `304 - NOT MODIFIED`.

- Exemplos:
- ETag: `51as54as` -> *Hash* (versão) da representação.
>Será diferente caso os dados mudem no servidor de origem.
- Status `304` - Caso representação não modificada.
>Resposta do servidor em caso de mesma versão do recurso.

##### **Shallow ETags = ETags simples**
- ETags simples, são tags que são calculadas de forma mais genérica e simples, geralmente com auxílio do framework,
como no caso de `org.springframework.web.filter.ShallowEtagHeaderFilter` por exemplo.

- É criado um bean para um filtro *HTTP Servlet* (`jakarta.servlet`) na aplicação,
que intercepta as requisições e gera o hash.

>*Essa implementação tem mais benefícios a nível de otimização de **rede**,
já que do lado do servidor, as consultas ao **banco de dados** são realizadas de qualquer maneira para recalcular as ETags.*

- Exemplo de bean para habilitar *Shallow ETags*:
```java
package com.algaworks.algafood.core.web;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.ShallowEtagHeaderFilter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import jakarta.servlet.Filter;

/**
* Deve implementar {@link WebMvcConfigurer} para configurar métodos de callback do Spring MVC.
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {

/**
* Bean para Shallow ETag (ETag simples), utilizando implementação do Spring.
*/
@Bean
public Filter shallowEtagFilter() {
return new ShallowEtagHeaderFilter();
}
}
```

##### Definido *headers* de controle de *cache* na resposta
- É possível definir cabeçalhos de controle de cache na resposta, a partir do `ResponseEntity` nos controladores.
- Exemplo (`Cache-Control: max-age=10, public`):
```java
public ResponseEntity> listar(ServletWebRequest request) {
Optional optEtag = calcularEtag(request,
formaPagamentoRepository.getDataUltimaAtualizacao());

if (optEtag.isPresent()) {
CollectionModel formasPagamentoModel = formaPagamentoModelAssembler
.toCollectionModel(formaPagamentoRepository.findAll());

return ResponseEntity.ok()
// -> adiciona o cabeçalho descrito acima, onde o cache terá duração de 10 segundos e será público
.cacheControl(CacheControl.maxAge(10, TimeUnit.SECONDS).cachePublic())
.eTag(optEtag.get(