API REST para gerenciamento de restaurantes, cardápios, usuários, grupos, permissões e pedidos. O projeto foi construído com Spring Boot e organiza a aplicação em camadas bem definidas, com persistência em MySQL, migrações via Flyway, documentação OpenAPI, envio de e-mails transacionais e armazenamento de fotos de produtos em disco local ou Amazon S3.
O sistema cobre o ciclo principal de um marketplace de alimentação:
- cadastro e manutenção de cozinhas, estados, cidades e formas de pagamento;
- gestão de restaurantes, responsáveis, produtos e fotos de produtos;
- cadastro de usuários, grupos e permissões;
- criação de pedidos, consulta com filtros e evolução do fluxo de status;
- emissão de estatísticas de vendas diárias;
- envio de e-mails quando pedidos são confirmados ou cancelados.
| Item | Uso no projeto |
|---|---|
| Java 21 | Linguagem principal |
| Spring Boot 3.5.3 | Base da aplicação |
| Spring Web | API REST |
| Spring Data JPA | Persistência e consultas |
| MySQL | Banco de dados principal |
| Flyway | Versionamento do schema |
| Bean Validation | Validação de entrada |
| ModelMapper | Conversão entre entidades e modelos da API |
| Spring Mail + FreeMarker | E-mails com template HTML |
| AWS SDK S3 | Armazenamento remoto de fotos |
| springdoc OpenAPI | Swagger UI e especificação da API |
| Rest Assured + JUnit 5 | Testes de integração |
O projeto segue uma separação clara de responsabilidades:
api: controladores, modelos de entrada e saída, assemblers e tratamento global de exceções.domain: entidades, regras de negócio, serviços, filtros, eventos, listeners e contratos de repositório.infrastructure: implementações de repositórios e integrações externas, como S3, SMTP e consultas customizadas.core: configurações transversais, como OpenAPI, serialização, CORS, validações, e-mail e storage.
Essa estrutura facilita manutenção, testes e evolução do domínio sem acoplar as regras de negócio diretamente à camada HTTP.
Cada restaurante possui:
- nome;
- taxa de frete;
- cozinha associada;
- endereço;
- formas de pagamento aceitas;
- usuários responsáveis;
- produtos;
- status de ativação e abertura.
Além do CRUD, a API permite ativar, inativar, abrir, fechar e administrar vínculos com responsáveis e meios de pagamento.
O pedido agrega:
- restaurante;
- cliente;
- forma de pagamento;
- endereço de entrega;
- itens;
- subtotal, taxa de frete e valor total;
- código UUID gerado automaticamente;
- status do fluxo.
Fluxo suportado:
CRIADO->CONFIRMADOCONFIRMADO->ENTREGUECRIADO->CANCELADO
Ao confirmar ou cancelar um pedido, a aplicação publica eventos de domínio e dispara e-mails com template HTML.
O upload de foto é feito por multipart/form-data, com validações explícitas:
- formatos aceitos:
image/jpegeimage/png; - tamanho máximo:
500KB.
O armazenamento pode ser local ou via Amazon S3, definido por configuração.
src/main/java/com/thomazllr/algafood
├── api
│ ├── assembler
│ ├── common
│ ├── controller
│ └── model
├── core
│ ├── email
│ ├── jackson
│ ├── modelmapper
│ ├── openapi
│ ├── storage
│ ├── validations
│ └── web
├── domain
│ ├── entity
│ ├── event
│ ├── exception
│ ├── filter
│ ├── listener
│ ├── repository
│ └── service
└── infrastructure
├── repository
└── service
src/main/resources
├── application.yml
├── db/migration
├── db/testdata
├── messages.properties
└── templates
src/test
├── java
└── resources
O schema é criado e evoluído com Flyway em src/main/resources/db/migration. As migrações presentes cobrem:
- criação inicial das entidades principais;
- cidades e estados;
- grupos, permissões e usuários;
- pedidos e itens de pedido;
- flags de ativação e abertura de restaurante;
- responsáveis de restaurante;
- código público do pedido;
- foto de produto;
- controle de atualização em forma de pagamento.
No perfil padrão, o Flyway também executa src/main/resources/db/testdata/afterMigrate.sql, que popula o banco com dados de exemplo. Isso é útil para desenvolvimento e demonstração.
- Java 21 instalado;
- MySQL em execução;
- acesso de leitura e escrita ao banco configurado;
- opcionalmente, credenciais de SMTP, Amazon S3 e Loggly.
O arquivo src/main/resources/application.yml define:
- banco principal em
jdbc:mysql://localhost:3306/algafood; - usuário
root; - senha
password333; - timezone padrão da aplicação em UTC;
- armazenamento de fotos com tipo
s3; - envio de e-mails com implementação
smtp.
| Variável | Finalidade |
|---|---|
API_EMAIL_KEY |
Senha da conta SMTP |
ID_CHAVE_ACESSO |
Access key do S3 |
CHAVE_ACESSO_SECRETA |
Secret key do S3 |
LOGGLY_TOKEN |
Token de envio de logs |
- O diretório configurado para storage local é
/home/thomazllr/Desktop/catalago, então em Windows ou outro ambiente será necessário ajustar esse caminho se quiser usar storage local. - Como o
application.ymlpadrão está apontando parasmtpes3, subir a aplicação sem credenciais válidas pode falhar ou deixar funcionalidades quebradas. - Para desenvolvimento local, faz sentido sobrescrever as configurações para usar
FAKEouSANDBOXno e-mail eLOCALno storage.
Crie um perfil local, por exemplo application-local.yml, com algo nesta linha:
spring:
datasource:
url: jdbc:mysql://localhost:3306/algafood?createDatabaseIfNotExist=true&serverTimezone=UTC
username: root
password: password333
algafood:
email:
impl: FAKE
storage:
tipo: LOCAL
local:
diretorio-fotos: C:/temp/algafood/catalogoDepois rode a aplicação com o perfil:
./mvnw spring-boot:run -Dspring-boot.run.profiles=localNo Windows PowerShell:
.\mvnw.cmd spring-boot:run "-Dspring-boot.run.profiles=local"Garanta que exista conectividade com:
- banco
algafood; - usuário
root; - senha
password333.
./mvnw spring-boot:runNo Windows PowerShell:
.\mvnw.cmd spring-boot:runCom a aplicação no ar, os endpoints de documentação ficam normalmente em:
http://localhost:8080/swagger-ui/index.htmlhttp://localhost:8080/v3/api-docs
| Recurso | Base |
|---|---|
| Cozinhas | /cozinhas |
| Cidades | /cidades |
| Estados | /estados |
| Formas de pagamento | /formas-pagamento |
| Grupos | /grupos |
| Usuários | /usuarios |
Esses recursos oferecem operações de listagem, busca por ID, criação, atualização e remoção.
| Operação | Rota |
|---|---|
| Listar restaurantes | GET /restaurantes |
| Buscar restaurante | GET /restaurantes/{id} |
| Criar restaurante | POST /restaurantes |
| Atualizar restaurante | PUT /restaurantes/{id} |
| Remover restaurante | DELETE /restaurantes/{id} |
| Ativar | PUT /restaurantes/{id}/ativo |
| Inativar | DELETE /restaurantes/{id}/ativo |
| Ativar em lote | PUT /restaurantes/ativacoes |
| Inativar em lote | DELETE /restaurantes/ativacoes |
| Abrir | PUT /restaurantes/{id}/abertura |
| Fechar | PUT /restaurantes/{id}/fechamento |
| Filtrar por frete grátis | GET /restaurantes/com-frete-gratis?nome=... |
| Operação | Rota |
|---|---|
| Listar formas aceitas | GET /restaurantes/{id}/formas-pagamento |
| Associar forma de pagamento | PUT /restaurantes/{id}/formas-pagamento/{formaPagamentoId} |
| Desassociar forma de pagamento | DELETE /restaurantes/{id}/formas-pagamento/{formaPagamentoId} |
| Operação | Rota |
|---|---|
| Listar responsáveis | GET /restaurantes/{restauranteId}/responsaveis |
| Associar responsável | PUT /restaurantes/{restauranteId}/responsaveis/{usuarioId} |
| Desassociar responsável | DELETE /restaurantes/{restauranteId}/responsaveis/{usuarioId} |
| Operação | Rota |
|---|---|
| Listar produtos do restaurante | GET /restaurantes/{id}/produtos |
| Listar incluindo inativos | GET /restaurantes/{id}/produtos?incluirInativos=true |
| Buscar produto | GET /restaurantes/{id}/produtos/{produtoId} |
| Criar produto | POST /restaurantes/{id}/produtos |
| Atualizar produto | PUT /restaurantes/{id}/produtos/{produtoId} |
| Operação | Rota |
|---|---|
| Enviar ou substituir foto | PUT /restaurantes/{restauranteId}/produtos/{produtoId}/foto |
| Consultar metadados da foto | GET /restaurantes/{restauranteId}/produtos/{produtoId}/foto com Accept: application/json |
| Baixar ou redirecionar para a imagem | GET /restaurantes/{restauranteId}/produtos/{produtoId}/foto com Accept: image/jpeg |
| Remover foto | DELETE /restaurantes/{restauranteId}/produtos/{produtoId}/foto |
Quando o storage estiver em S3, a API pode responder com redirecionamento 302 Found para a URL pública do objeto.
| Operação | Rota |
|---|---|
| Listar permissões do grupo | GET /grupos/{grupoId}/permissoes |
| Associar permissão | PUT /grupos/{grupoId}/permissoes/{permissaoId} |
| Desassociar permissão | DELETE /grupos/{grupoId}/permissoes/{permissaoId} |
| Operação | Rota |
|---|---|
| Listar grupos do usuário | GET /usuarios/{usuarioId}/grupos |
| Associar grupo | PUT /usuarios/{usuarioId}/grupos/{grupoId} |
| Desassociar grupo | DELETE /usuarios/{usuarioId}/grupos/{grupoId} |
| Atualizar senha | PUT /usuarios/{id}/senha |
| Operação | Rota |
|---|---|
| Listar pedidos com paginação | GET /pedidos |
| Buscar pedido por código | GET /pedidos/{codigoPedido} |
| Criar pedido | POST /pedidos |
| Confirmar pedido | PUT /pedidos/{codigoPedido}/confirmacao |
| Marcar como entregue | PUT /pedidos/{codigoPedido}/entregar |
| Cancelar pedido | PUT /pedidos/{codigoPedido}/cancelamento |
Filtros disponíveis em GET /pedidos:
clienteIdrestauranteIddataCriacaoIniciodataCriacaoFim
| Operação | Rota |
|---|---|
| Vendas diárias | GET /estatisticas/vendas-diarias |
Filtros disponíveis:
restauranteIddataCriacaoIniciodataCriacaoFimtimeOffset
O parâmetro timeOffset é normalizado pelo controlador e aceita formatos como -03:00 e -0300.
GET /cozinhasretorna paginação.GET /pedidosretorna paginação com filtros.- Objetos
Pagesão serializados em um formato customizado com os camposcontent,size,totalElements,totalPagesenumber. GET /formas-pagamentousaETageCache-Controlcom cache de 10 segundos.- O projeto registra
ShallowEtagHeaderFiltere libera CORS para todos os caminhos e métodos.
Os erros da API seguem um payload padronizado com campos como:
statustypetitledetailuserMessagetimestampfieldspara erros de validação
Exemplo:
{
"status": 400,
"type": "https://algafood.com.br/dados-invalidos",
"title": "Dados inválidos.",
"detail": "Um ou mais campos estão inválidos. Corrija e informe os valores corretos e tente novamente.",
"userMessage": "Um ou mais campos estão inválidos. Corrija e informe os valores corretos e tente novamente.",
"timestamp": "2026-08-20T12:00:00Z",
"fields": [
{
"name": "nome",
"message": "Nome da cozinha é obrigatório"
}
]
}Os testes estão em src/test e hoje incluem principalmente um teste de integração para o recurso de cozinhas, usando:
@SpringBootTestcom porta aleatória;- Rest Assured para chamadas HTTP reais;
DatabaseCleanerpara limpar o banco entre execuções.
O perfil test usa o banco:
jdbc:mysql://localhost:3306/algafood_test
Há um cuidado importante no DatabaseCleaner: ele só limpa bases cujo nome termina com test. Isso evita apagar dados de um banco de desenvolvimento por engano.
./mvnw testNo Windows PowerShell:
.\mvnw.cmd test- A aplicação define o fuso padrão interno como UTC no
main. - As mensagens de validação estão externalizadas em
messages.properties, com textos em português. - O projeto já possui templates HTML para e-mail de confirmação e cancelamento de pedido.
- O código usa repositórios customizados e
Specificationpara filtros dinâmicos, especialmente em pedidos e restaurantes. - O perfil padrão injeta massa de dados automaticamente após as migrações, o que facilita testes manuais da API.
Este projeto é uma API REST relativamente completa para estudo e evolução de um domínio de delivery/restaurantes. Ele já inclui boa parte da infraestrutura comum de aplicações reais: versionamento de banco, tratamento padronizado de erros, validações, paginação, filtros, cache HTTP, upload de arquivos, storage externo, eventos de domínio, templates de e-mail e documentação OpenAPI.
Para começar sem atrito, o melhor caminho costuma ser:
- subir um MySQL local;
- criar um perfil de desenvolvimento com
email.impl=FAKEestorage.tipo=LOCAL; - iniciar a aplicação com o Maven Wrapper;
- explorar a API pelo Swagger UI;
- usar os dados semeados automaticamente no perfil padrão para validar os fluxos.