Project developed during the AlgaWorks Spring Boot specialization course. REST API inspired by the food delivery domain, built with Spring Boot 3.5.7, Java 21, and Hexagonal Architecture (Ports & Adapters).
- Overview
- Tech Stack
- Architecture
- Project Structure
- Domain Models
- API Endpoints
- Exception Handling
- Validation
- Database Migrations (Flyway)
- How to Run
- Tests
- Implemented Features
vifood is a food delivery REST API for managing restaurants, menus, orders, payment methods, users, and permissions. The project applies modern best practices such as Hexagonal Architecture, Bean Validation, global exception handling, pagination, dynamic filters via JPA Specifications, and versioned database migrations with Flyway.
| Technology | Version | Purpose |
|---|---|---|
| Java | 21 | Main language |
| Spring Boot | 3.5.7 | Base framework |
| Spring Web | β | REST API |
| Spring Data JPA | β | Persistence |
| Spring Validation | β | Bean Validation (Jakarta) |
| Hibernate | β | ORM |
| PostgreSQL | 42.7.7 | Database |
| Flyway | 10.15.0 | Schema versioning |
| Lombok | β | Boilerplate reduction |
| Thymeleaf | β | Template engine |
| Flying Saucer | 9.1.22 | PDF generation (HTML β PDF) |
| iText | 2.1.7 | PDF library |
| H2 Database | β | In-memory DB (testing) |
| Maven | β | Build tool |
| Docker Compose | β | Local orchestration |
Application port: 8081
The project follows Hexagonal Architecture (Ports & Adapters) with three well-defined layers:
ββββββββββββββββββββββββββββββββββββββββββββββββββ
β INFRA (Adapters) β
β REST Controllers | JPA Adapters | DTOs β
β Mappers | Configurations | Exception Handler β
ββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ
β implements
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββ
β APPLICATION (Use Cases) β
β Services that implement the inbound ports β
ββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ
β depends on
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββ
β DOMAIN (Core) β
β Entities | Ports (in/out) | Exceptions β
β Filters | No Spring dependencies β
ββββββββββββββββββββββββββββββββββββββββββββββββββ
- Domain β Business core. Entities, business rules, ports (interfaces), and business exceptions. No Spring dependencies.
- Application β Use case implementations (Services). Orchestrates domain logic and uses outbound ports.
- Infra β Adapters: REST Controllers, JPA repositories, DTOs, mappers, configurations, exception handlers.
src/main/java/com/vitoriadeveloper/vifood/
β
βββ domain/
β βββ model/ # JPA entities (Restaurant, Kitchen, Orderβ¦)
β βββ ports/
β β βββ in/ # Inbound ports (use cases)
β β βββ out/ # Outbound ports (repositories)
β βββ exceptions/ # Business exceptions
β βββ filters/ # Filters (RestaurantFilter, OrderFilter)
β
βββ application/
β βββ services/ # Use case implementations
β βββ config/ # ValidationConfig
β
βββ infra/
βββ adapters/
β βββ http/ # REST Controllers
β βββ repositories/ # JPA Adapters
β βββ model/
β βββ dto/
β β βββ request/ # Input DTOs
β β βββ response/ # Output DTOs
β βββ mapper/ # Manual mappers
βββ repositories/ # Spring Data JPA Repositories
β βββ spec/ # Specifications (dynamic filters)
βββ validation/ # Validation groups
βββ config/ # JpaConfig
βββ exceptions/ # ApiExceptionHandler
βββ utils/ # ErrorResponse
The core entity. Has name, delivery fee, status (active/inactive, open/closed), creation/update timestamps, embedded address, kitchen, payment methods, products, and owners.
| Field | Type | Description |
|---|---|---|
id |
UUID | Identifier |
nome |
String | Restaurant name |
taxaFrete |
BigDecimal | Delivery fee |
ativo |
Boolean | Active flag |
aberto |
Boolean | Open flag |
dataCadastro |
OffsetDateTime | Creation timestamp |
dataAtualizacao |
OffsetDateTime | Last update timestamp |
cozinha |
Kitchen (ManyToOne) | Kitchen type |
endereco |
Address (Embedded) | Address |
formasPagamento |
List | Accepted payments |
produtos |
List | Menu |
responsaveis |
List | Owners/managers |
Methods: open(), close(), inactive(), active(), associateRestaurantOwner(), disassociateRestaurantOwner().
Kitchen type/category (Italian, Japanese, etc.). OneToMany relationship with Restaurant.
Menu item belonging to a restaurant. Has name, description, price, active flag. Can have an image (OneToOne with ProductImage).
Order placed by a client at a restaurant. Has items, status (state machine), total value, and delivery address.
Order status (state machine):
CRIADO β CONFIRMADO β PREPARANDO β PRONTO β SAIU_PARA_ENTREGA β ENTREGUE
(created) (confirmed) (preparing) (ready) (out for delivery) (delivered)
β
CANCELADO (from CRIADO, CONFIRMADO, or PREPARANDO)
(cancelled)
Methods: addItem(), removeItem(), confirmOrder(), cancelOrder(), changeStatus(), calculateTotalValue().
Line item in an order. Quantity, unit price, total price, and notes. Computes total via calculateTotal().
System user. Has name, unique email, password. Belongs to multiple GroupPermission entries (ManyToMany).
Permission group. Holds multiple UserPermission entries.
Atomic system permission (name + description).
Federative unit: name + 2-letter code.
City linked to a State.
ZIP code, street, number, complement, neighborhood, city. Used in Restaurant and Order.
Accepted payment method (description).
| Method | Path | Description |
|---|---|---|
| GET | /estados |
List all states |
| GET | /estados/{id} |
Get state by ID |
| DELETE | /estados/{id} |
Delete state |
| Method | Path | Description |
|---|---|---|
| POST | /cidades |
Create city |
| GET | /cidades |
List cities |
| GET | /cidades/{id} |
Get by ID |
| PUT | /cidades/{id} |
Update |
| DELETE | /cidades/{id} |
Delete |
| Method | Path | Description |
|---|---|---|
| POST | /cozinhas |
Create kitchen |
| GET | /cozinhas?page=0&size=10 |
List (paginated) |
| GET | /cozinhas/{id} |
Get by ID |
| PUT | /cozinhas/{id} |
Update |
| DELETE | /cozinhas/{id} |
Delete |
| Method | Path | Description |
|---|---|---|
| POST | /restaurantes |
Create restaurant |
| GET | /restaurantes |
List (filters: nome, taxaFreteMin, taxaFreteMax) |
| GET | /restaurantes/{id} |
Get by ID |
| PUT | /restaurantes/{id} |
Update |
| PATCH | /restaurantes/{id} |
Partial update |
| DELETE | /restaurantes/{id} |
Delete |
| PUT | /restaurantes/{id}/ativar |
Activate |
| PUT | /restaurantes/{id}/inativar |
Deactivate |
| POST | /restaurantes/batch/ativar |
Batch activate |
| POST | /restaurantes/batch/inativar |
Batch deactivate |
| POST | /restaurantes/{idRestaurante}/formas-pagamento |
Associate payment method |
| DELETE | /restaurantes/{idRestaurante}/formas-pagamento/{idFormaPagamento} |
Disassociate payment method |
| GET | /restaurantes/{idRestaurante}/responsaveis |
List owners |
| POST | /restaurantes/{idRestaurante}/responsaveis/{idResponsavel} |
Add owner |
| DELETE | /restaurantes/{idRestaurante}/responsaveis/{idResponsavel} |
Remove owner |
| GET | /restaurantes/{idRestaurante}/produtos |
List products |
| POST | /restaurantes/{idRestaurante}/produtos |
Add product |
| PUT | /restaurantes/{idRestaurante}/produtos/{idProduto} |
Update product |
| DELETE | /restaurantes/{idRestaurante}/produtos/{idProduto} |
Delete product |
| Method | Path | Description |
|---|---|---|
| GET | /formas-pagamento |
List |
| POST | /formas-pagamento |
Create |
| GET | /formas-pagamento/{id} |
Get |
| DELETE | /formas-pagamento/{id} |
Delete |
| Method | Path | Description |
|---|---|---|
| GET | /usuarios |
List users |
| POST | /usuarios |
Create user |
| GET | /usuarios/{id} |
Get by ID |
| DELETE | /usuarios/{id} |
Delete |
| PUT | /usuarios/{usuarioId}/{grupoId} |
Associate user with group |
| DELETE | /usuarios/{usuarioId}/{grupoId} |
Disassociate user from group |
| Method | Path | Description |
|---|---|---|
| GET | /permissoes |
List |
| POST | /permissoes |
Create |
| GET | /permissoes/{id} |
Get |
| PUT | /permissoes/{id} |
Update |
| DELETE | /permissoes/{id} |
Delete |
| Method | Path | Description |
|---|---|---|
| GET | /grupos |
List |
| POST | /grupos |
Create |
| GET | /grupos/{id} |
Get |
| PUT | /grupos/{id} |
Update |
| DELETE | /grupos/{id} |
Delete |
| POST | /grupos/{grupoId}/permissoes/{permissaoId} |
Add permission |
| DELETE | /grupos/{grupoId}/permissoes/{permissaoId} |
Remove permission |
| Method | Path | Description |
|---|---|---|
| POST | /pedidos |
Create order |
| GET | /pedidos/{pedidoId} |
Get by ID |
| GET | /pedidos/cliente/{clienteId} |
Orders by client |
| GET | /pedidos/restaurante/{restauranteId} |
Orders by restaurant |
| GET | /pedidos/filtros |
Filter (clienteId, restauranteId, status) with pagination |
| PATCH | /pedidos/{pedidoId} |
Partial update |
| DELETE | /pedidos/{pedidoId} |
Delete |
| PUT | /pedidos/{pedidoId}/confirmar |
Confirm (CRIADO β CONFIRMADO) |
| PUT | /pedidos/{pedidoId}/cancelar |
Cancel order |
| PATCH | /pedidos/{pedidoId}/status?status={status} |
Change status |
| Method | Path | Description |
|---|---|---|
| POST | /estatisticas |
Generate statistics |
| GET | /estatisticas |
Query statistics |
BusinessExceptionβ Base for business rule violationsKitchenNotFoundException,RestaurantNotFoundExceptionCityNotFoundException,StateNotFoundExceptionUserNotFoundException,OrderNotFoundExceptionProductNotFoundException,PaymentMethodNotFoundExceptionGroupPermissionNotFoundException,PermissionNotFoundExceptionInvalidStateReferenceException
Annotated with @RestControllerAdvice, extends ResponseEntityExceptionHandler, and converts exceptions into standardized HTTP responses:
| Exception | Status |
|---|---|
*NotFoundException |
404 Not Found |
DataIntegrityViolationException |
409 Conflict |
MethodArgumentNotValidException |
400 Bad Request (with field details) |
HttpMessageNotReadableException |
400 Bad Request |
InvalidFormatException |
400 Bad Request |
PropertyBindingException |
400 Bad Request |
MethodArgumentTypeMismatchException |
400 Bad Request |
{
"timestamp": "2026-05-01T10:00:00-03:00",
"status": 400,
"title": "Invalid data",
"message": "One or more fields are invalid",
"fields": { "nome": "must not be blank" }
}- Bean Validation (Jakarta):
@NotBlank,@NotNull,@Valid,@PositiveOrZero - Validation Groups (
infra/validation/Groups.java):Groups.CozinhaIdfor conditional validation with@ConvertGroup - Cascading validation with
@Validon nested fields - Strict Jackson:
FAIL_ON_UNKNOWN_PROPERTIES=truerejects unknown JSON fields
| Version | Description |
|---|---|
| V001 | Initial schema: kitchens, states, cities, restaurants, products, payment methods, users, groups, permissions, and join tables |
| V002 | Restaurant flags and timestamps (active, open, dataCadastro, dataAtualizacao), embedded address, product description |
| V003 | tb_pedidos and tb_itens_pedido tables |
| V004 | Product FK on order items + delivery address on orders |
| V005 | Restaurant FK on orders |
| V006 | Restaurant timestamps converted to TIMESTAMP WITH TIME ZONE |
| V007 | tb_restaurante_responsaveis table (restaurant owners) |
| V008 | Product images table (tb_produto_imagens) |
- Java 21
- Maven 3.9+
- Docker (for PostgreSQL)
docker-compose up -d./mvnw spring-boot:runOr on Windows:
mvnw.cmd spring-boot:runThe API will be available at http://localhost:8081.
- Database: PostgreSQL on
localhost:5432, databasevifood - HikariCP pool: max 5, min idle 3
- DDL:
validate(Hibernate only validates; Flyway handles schema)
| Class | Type | What it tests |
|---|---|---|
VifoodApplicationTests |
Smoke | Spring context loading |
RestaurantControllerTests |
Integration | CRUD, filters, activation/deactivation |
KitchenControllerTests |
Integration | CRUD, pagination |
KitchenServiceIT |
Integration | Service logic, transactions |
The maven-failsafe plugin (v3.2.5) runs integration tests (suffix IT).
Run tests:
./mvnw test
./mvnw verify # includes integration testsβ Full Restaurant CRUD with dynamic filters (name, delivery fee range) β Open/close and activate/deactivate operations (single and batch) β Menu (Products) management per restaurant (with image upload) β Complete Order system with a 7-state state machine β Payment Methods management and association with restaurants β Hierarchical Location management (States β Cities) β Restaurant categorization via Kitchens β Users, Groups, and Permissions system β Restaurant owners (managers) β Pagination for kitchens and orders listings β Dynamic filters via JPA Specifications β Validation with Bean Validation + groups β Global exception handling with localized messages β Versioned migrations via Flyway (8 migrations) β PDF generation (Flying Saucer + Thymeleaf) β Statistics and reporting
VitΓ³ria Carolina Dantas Project developed as part of the AlgaWorks Spring Boot specialization course.
π Last updated: May/2026