Loading repository dataβ¦
Loading repository dataβ¦
nanotaboada / repository
π§ͺ Proof of Concept for a RESTful Web Service built with Java / JDK 25 (LTS) and Spring Boot 4
Proof of Concept for a RESTful Web Service built with Spring Boot 4 targeting JDK 25 (LTS). This project demonstrates best practices for building a layered, testable, and maintainable API implementing CRUD operations for a Players resource (Argentina 2022 FIFA World Cup squad).
| Component | Technology |
|---|---|
| Framework | Spring Boot 4.0.0 |
| Runtime | Java (JDK 25 LTS) |
| Build Tool | Maven |
| Database (Runtime) | SQLite |
| Database (Tests) | SQLite (in-memory) |
| ORM | Hibernate / Spring Data JPA |
| API Documentation | SpringDoc OpenAPI |
| Testing | JUnit 5 + Mockito + AssertJ |
| Code Coverage | JaCoCo |
| Containerization | Docker + Docker Compose |
| CI/CD | GitHub Actions |
π‘ Note: Maven wrapper (
./mvnw) is included, so Maven installation is optional.
Layered architecture with dependency injection via Spring Boot's IoC container and constructor injection using Lombok's @RequiredArgsConstructor.
%%{init: {
"theme": "default",
"themeVariables": {
"fontFamily": "Fira Code, Consolas, monospace",
"textColor": "#555",
"lineColor": "#555",
"clusterBkg": "#f5f5f5",
"clusterBorder": "#ddd"
}
}}%%
graph RL
tests[tests]
subgraph Layer1[" "]
Application[Application]
SpringBoot[Spring Boot]
SpringDoc[SpringDoc]
end
subgraph Layer2[" "]
controllers[controllers]
SpringValidation[Spring Validation]
end
subgraph Layer3[" "]
services[services]
ModelMapper[ModelMapper]
SpringCache[Spring Cache]
end
subgraph Layer4[" "]
repositories[repositories]
SpringDataJPA[Spring Data JPA]
end
models[models]
JakartaPersistence[Jakarta Persistence]
ProjectLombok[Lombok]
%% Strong dependencies
%% Layer 1
SpringBoot --> Application
SpringDoc --> Application
controllers --> Application
services --> Application
repositories --> Application
%% Layer 2
SpringValidation --> controllers
services --> controllers
models --> controllers
%% Layer 3
SpringCache --> services
ModelMapper --> services
repositories --> services
models --> services
%% Layer 4
SpringDataJPA --> repositories
models --> repositories
%% Soft dependencies
controllers -.-> tests
services -.-> tests
repositories -.-> tests
%% Cross-cutting
JakartaPersistence --> models
ProjectLombok --> models
%% Node styling with stroke-width
classDef core fill:#b3d9ff,stroke:#6db1ff,stroke-width:2px,color:#555,font-family:monospace;
classDef feat fill:#ffffcc,stroke:#fdce15,stroke-width:2px,color:#555,font-family:monospace;
classDef deps fill:#ffcccc,stroke:#ff8f8f,stroke-width:2px,color:#555,font-family:monospace;
classDef test fill:#ccffcc,stroke:#53c45e,stroke-width:2px,color:#555,font-family:monospace;
class Application,models,repositories,services,controllers core
class SpringBoot,SpringDataJPA,SpringCache,SpringValidation feat
class JakartaPersistence,ProjectLombok,ModelMapper,SpringDoc deps
class tests test
Arrows follow the injection direction (A β B means A is injected into B). Solid = runtime dependency, dotted = structural. Blue = core domain, red = third-party, green = tests.
Architecturally significant decisions are documented as Architecture Decision Records (ADRs) in docs/adr/. Each record captures the context, the alternatives considered, and the trade-offs of the choice β the "why" behind the implementation.
| ADR | Decision |
|---|---|
| ADR-0001 | Adopt Spring Boot as REST API Framework |
| ADR-0002 | Use Spring Data JPA with SQLite |
| ADR-0003 | Implement In-Memory Caching with Spring Cache |
| ADR-0004 | Adopt Layered Architecture |
See the full ADR index for all 12 records.
Interactive API documentation is available via Swagger UI at http://localhost:9000/swagger/index.html when the server is running.
| Method | Endpoint | Description | Status |
|---|---|---|---|
GET | /players | List all players | 200 OK |
GET | /players/{id} | Get player by UUID | 200 OK |
GET | /players/search/league/{league} | Search players by league | 200 OK |
GET | /players/squadnumber/{squadNumber} | Get player by squad number | 200 OK |
POST | /players | Create new player | 201 Created |
PUT | /players/{squadNumber} | Update player by squad number | 204 No Content |
DELETE | /players/{squadNumber} | Remove player by squad number | 204 No Content |
GET | /actuator/health | Health check | 200 OK |
Error codes: 400 Bad Request (validation failed) Β· 404 Not Found (player not found) Β· 409 Conflict (duplicate squad number on POST)
For complete endpoint documentation with request/response schemas, explore the interactive Swagger UI. You can also access the OpenAPI JSON specification at http://localhost:9000/docs.
Before you begin, ensure you have the following installed:
./mvnw)π‘ Note: macOS users may need to set
JAVA_HOME:export JAVA_HOME=$(/usr/libexec/java_home -v 25)
git clone https://github.com/nanotaboada/java.samples.spring.boot.git
cd java.samples.spring.boot
./mvnw clean package
./mvnw spring-boot:run
Once the application is running, you can access:
http://localhost:9000http://localhost:9000/swagger/index.htmlhttp://localhost:9000/docshttp://localhost:9001/actuator/healthdocker compose up
# or detached mode
docker compose up -d
Exposed Ports:
9000 - Main API server9001 - Actuator management endpointsπ‘ Note: The Docker container uses a pre-seeded SQLite database with Argentina 2022 FIFA World Cup squad data. On first run, the database is copied from the image to a named volume (
java-samples-spring-boot_storage) ensuring data persistence across container restarts.
docker compose down
To reset the database to its initial state:
docker compose down -v # Remove volumes
docker compose up # Fresh start with seed data
Each release publishes multiple tags for flexibility:
# By semantic version (recommended for production)
docker pull ghcr.io/nanotaboada/java-samples-spring-boot:1.0.0
# By club name (memorable alternative)
docker pull ghcr.io/nanotaboada/java-samples-spring-boot:arsenal
# Latest release
docker pull ghcr.io/nanotaboada/java-samples-spring-boot:latest
Configured in src/main/resources/application.properties:
server.port=9000
management.server.port=9001
spring.datasource.url=jdbc:sqlite:storage/players-sqlite3.db
springdoc.swagger-ui.path=/swagger/index.html
springdoc.api-docs.path=/docs
Configured in src/test/resources/application.properties:
spring.datasource.url=jdbc:sqlite::memory:
spring.jpa.hibernate.ddl-auto=create-drop
Schema versioning is managed by Flyway, which runs automatically on application startup and applies any pending migrations in order.
Versioned SQL scripts live in src/main/resources/db/migration/ and follow the Flyway naming convention:
V{version}__{description}.sql
| File | Description |
|---|---|
V1__Create_players_table.sql | Creates the players table (schema) |
V2__Seed_starting11.sql | Seeds 11 Starting XI players (starting11 = 1) |
V3__Seed_substitutes.sql | Seeds 15 Substitute players (starting11 = 0) |
All migration SQL is written to be compatible with both SQLite (local dev) and PostgreSQL (see #286).
On first run, Flyway detects an empty database and applies V1 β V2 β V3 in sequence, creating the players table and seeding all 26 players. The database file (storage/players-sqlite3.db) is created automatically and is excluded from version control.
Create a new file in src/main/resources/db/migration/ with the next version number:
touch src/main/resources/db/migration/V4__Add_nationality_column.sql
Flyway applies it automatically on the next application startup. View the applied history by querying the flyway_schema_history table.
Delete the SQLite file and restart β Flyway recreates the schema an