Skip to content

Commit 572c758

Browse files
authored
Merge pull request #297 from nanotaboada/docs/296-normalize-readme-structure
docs: normalize README structure and add RELEASES.md
2 parents 7d39cc2 + 662e7cf commit 572c758

3 files changed

Lines changed: 146 additions & 242 deletions

File tree

.markdownlint.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"MD013": false,
3+
"MD024": {
4+
"siblings_only": true
5+
}
6+
}

README.md

Lines changed: 66 additions & 242 deletions
Original file line numberDiff line numberDiff line change
@@ -13,40 +13,12 @@
1313

1414
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).
1515

16-
## Table of Contents
17-
18-
- [Features](#features)
19-
- [Tech Stack](#tech-stack)
20-
- [Project Structure](#project-structure)
21-
- [Architecture](#architecture)
22-
- [API Reference](#api-reference)
23-
- [Prerequisites](#prerequisites)
24-
- [Quick Start](#quick-start)
25-
- [Clone](#clone)
26-
- [Build](#build)
27-
- [Run](#run)
28-
- [Access](#access)
29-
- [Testing](#testing)
30-
- [Containers](#containers)
31-
- [Build and Start](#build-and-start)
32-
- [Stop](#stop)
33-
- [Reset Database](#reset-database)
34-
- [Environment Variables](#environment-variables)
35-
- [Command Summary](#command-summary)
36-
- [Releases](#releases)
37-
- [Contributing](#contributing)
38-
- [Legal](#legal)
39-
4016
## Features
4117

42-
- 🔌 **RESTful API** - Full CRUD operations for Players resource
4318
- 📚 **Clean Architecture** - Layered design with clear separation of concerns
19+
- 📝 **Interactive Documentation** - Live API exploration and testing interface
4420
- 🚦 **Input Validation** - Bean Validation (JSR-380) constraints
4521
-**Performance Caching** - Optimized data retrieval with cache annotations
46-
- 🔍 **Advanced Search** - League search with JPQL and squad number lookup with derived queries
47-
- 📝 **Interactive Documentation** - Live API exploration and testing interface
48-
- 🩺 **Health Monitoring** - Application health and metrics endpoints
49-
-**Comprehensive Testing** - High code coverage with automated reporting
5022
- 🐳 **Containerized Deployment** - Multi-stage builds with pre-seeded database
5123
- 🔄 **Automated Pipeline** - Continuous integration with automated testing and builds
5224

@@ -68,31 +40,6 @@ Proof of Concept for a RESTful Web Service built with **Spring Boot 4** targetin
6840

6941
> 💡 **Note:** Maven wrapper (`./mvnw`) is included, so Maven installation is optional.
7042
71-
## Project Structure
72-
73-
```tree
74-
src/main/java/ar/com/nanotaboada/java/samples/spring/boot/
75-
├── Application.java # Main entry point, @SpringBootApplication
76-
├── controllers/ # REST endpoints (@RestController)
77-
│ └── PlayersController.java
78-
├── services/ # Business logic (@Service, caching)
79-
│ └── PlayersService.java
80-
├── repositories/ # Data access (@Repository, Spring Data JPA)
81-
│ └── PlayersRepository.java
82-
├── models/ # Domain entities & DTOs
83-
│ ├── Player.java # JPA entity
84-
│ └── PlayerDTO.java # Data Transfer Object with validation
85-
└── converters/ # Infrastructure converters
86-
└── IsoDateConverter.java # JPA converter for ISO-8601 dates
87-
88-
src/test/java/.../test/
89-
├── controllers/ # Controller tests (@WebMvcTest)
90-
├── services/ # Service layer tests
91-
├── repositories/ # Repository tests (@DataJpaTest)
92-
├── PlayerFakes.java # Test data factory for Player entities
93-
└── PlayerDTOFakes.java # Test data factory for PlayerDTO
94-
```
95-
9643
## Architecture
9744

9845
Layered architecture with dependency injection via Spring Boot's IoC container and constructor injection using Lombok's `@RequiredArgsConstructor`.
@@ -187,44 +134,26 @@ graph RL
187134
class tests test
188135
```
189136

190-
*Simplified, conceptual view — not all components or dependencies are shown.*
191-
192-
### Arrow Semantics
193-
194-
Arrows follow the injection direction: `A --> B` means A is injected into B. Solid arrows (`-->`) represent active Spring dependencies — beans wired by the IoC container and invoked at runtime. Dotted arrows (`-.->`) represent test dependencies — test classes reference the types they exercise but are not injected into them.
195-
196-
### Composition Root Pattern
197-
198-
`Application` is the composition root: `@SpringBootApplication` triggers component scanning that discovers and registers all beans, `@EnableCaching` activates the caching infrastructure, and `@Bean ModelMapper` declares the mapping dependency explicitly. Constructor injection is enforced throughout via Lombok's `@RequiredArgsConstructor` on `final` fields.
199-
200-
### Layered Architecture
201-
202-
Four layers: Initialization (`Application`), HTTP (`controllers`), Business (`services`), and Data (`repositories`).
203-
204-
Spring and third-party packages are placed inside the subgraph of the layer that uses them — co-residency communicates the relationship without extra arrows: `Spring Boot` and `SpringDoc` in Initialization, `Spring Validation` in HTTP, `Spring Cache` and `ModelMapper` in Business, `Spring Data JPA` in Data.
205-
206-
`models` and `converters` are cross-cutting: `models` defines the JPA entity and DTOs shared across all layers; `converters` holds the `AttributeConverter` that handles ISO-8601 date serialization for `models`. `Jakarta Persistence` and `Lombok` are their respective dependencies. `Lombok` is also used in `services` and `controllers` via `@RequiredArgsConstructor` and `@Slf4j`, though those arrows are omitted for clarity.
207-
208-
### Color Coding
209-
210-
Blue = core application packages, yellow = Spring ecosystem, red = third-party libraries, green = tests.
137+
> *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.*
211138
212139
## API Reference
213140

214141
Interactive API documentation is available via Swagger UI at `http://localhost:9000/swagger/index.html` when the server is running.
215142

216-
**Quick Reference:**
143+
| Method | Endpoint | Description | Status |
144+
| ------ | -------- | ----------- | ------ |
145+
| `GET` | `/players` | List all players | `200 OK` |
146+
| `GET` | `/players/{id}` | Get player by ID | `200 OK` |
147+
| `GET` | `/players/search/league/{league}` | Search players by league | `200 OK` |
148+
| `GET` | `/players/squadnumber/{squadNumber}` | Get player by squad number | `200 OK` |
149+
| `POST` | `/players` | Create new player | `201 Created` |
150+
| `PUT` | `/players/{id}` | Update player by ID | `200 OK` |
151+
| `DELETE` | `/players/{id}` | Remove player by ID | `204 No Content` |
152+
| `GET` | `/actuator/health` | Health check | `200 OK` |
217153

218-
- `GET /players` - List all players
219-
- `GET /players/{id}` - Get player by ID
220-
- `GET /players/search/league/{league}` - Search players by league
221-
- `GET /players/squadnumber/{squadNumber}` - Get player by squad number
222-
- `POST /players` - Create new player
223-
- `PUT /players/{id}` - Update existing player
224-
- `DELETE /players/{id}` - Remove player
225-
- `GET /actuator/health` - Health check
154+
Error codes: `400 Bad Request` (validation failed) · `404 Not Found` (player not found) · `409 Conflict` (duplicate squad number on `POST`)
226155

227-
For complete endpoint documentation with request/response schemas, explore the [interactive Swagger UI](http://localhost:9000/swagger/index.html). You can also access the OpenAPI JSON specification at `http://localhost:9000/v3/api-docs`.
156+
For complete endpoint documentation with request/response schemas, explore the [interactive Swagger UI](http://localhost:9000/swagger/index.html). You can also access the OpenAPI JSON specification at `http://localhost:9000/docs`.
228157

229158
## Prerequisites
230159

@@ -267,42 +196,9 @@ Once the application is running, you can access:
267196

268197
- **API Server**: `http://localhost:9000`
269198
- **Swagger UI**: `http://localhost:9000/swagger/index.html`
270-
- **OpenAPI Spec**: `http://localhost:9000/v3/api-docs`
199+
- **OpenAPI Spec**: `http://localhost:9000/docs`
271200
- **Health Check**: `http://localhost:9001/actuator/health`
272201

273-
## Testing
274-
275-
Run the full test suite with coverage:
276-
277-
```bash
278-
./mvnw verify
279-
```
280-
281-
**View Coverage Report:**
282-
283-
```bash
284-
open target/site/jacoco/index.html
285-
```
286-
287-
**Test Structure:**
288-
289-
- **Unit Tests** - `@WebMvcTest`, `@DataJpaTest` for isolated layer testing (with `@AutoConfigureCache` for caching support)
290-
- **Test Database** - SQLite in-memory (jdbc:sqlite::memory:) for fast, isolated test execution
291-
- **Mocking** - Mockito with `@MockitoBean` for dependency mocking
292-
- **Assertions** - AssertJ fluent assertions
293-
- **Naming Convention** - `givenX_whenY_thenZ` BDD pattern:
294-
- `givenPlayersExist_whenGetAll_thenReturnsOkWithAllPlayers()`
295-
- `givenSquadNumberExists_whenPost_thenReturnsConflict()`
296-
- `givenPlayerExists_whenFindById_thenReturnsPlayer()`
297-
298-
**Coverage Targets:**
299-
300-
- Controllers: 100%
301-
- Services: 100%
302-
- Repositories: Custom query methods (interfaces excluded by JaCoCo design)
303-
304-
> 💡 **Note:** Dates are stored as ISO-8601 strings for SQLite compatibility. A JPA `AttributeConverter` handles LocalDate ↔ ISO-8601 string conversion transparently. Tests use SQLite in-memory database (jdbc:sqlite::memory:) - the converter works seamlessly with both file-based and in-memory SQLite.
305-
306202
## Containers
307203

308204
### Build and Start
@@ -335,44 +231,72 @@ docker compose down -v # Remove volumes
335231
docker compose up # Fresh start with seed data
336232
```
337233

234+
### Pull Docker images
235+
236+
Each release publishes multiple tags for flexibility:
237+
238+
```bash
239+
# By semantic version (recommended for production)
240+
docker pull ghcr.io/nanotaboada/java-samples-spring-boot:1.0.0
241+
242+
# By club name (memorable alternative)
243+
docker pull ghcr.io/nanotaboada/java-samples-spring-boot:arsenal
244+
245+
# Latest release
246+
docker pull ghcr.io/nanotaboada/java-samples-spring-boot:latest
247+
```
248+
338249
## Environment Variables
339250

340-
### Development (Local)
251+
### Development
341252

342-
Configuration in `src/main/resources/application.properties`:
253+
Configured in `src/main/resources/application.properties`:
343254

344255
```properties
345-
# Server Configuration
346256
server.port=9000
347257
management.server.port=9001
348-
349-
# Database Configuration (SQLite)
350258
spring.datasource.url=jdbc:sqlite:storage/players-sqlite3.db
351-
spring.datasource.driver-class-name=org.sqlite.JDBC
352-
spring.jpa.database-platform=org.hibernate.community.dialect.SQLiteDialect
353-
spring.jpa.hibernate.ddl-auto=none
354-
355-
# Caching
356-
spring.cache.type=simple
357-
358-
# OpenAPI Documentation
359-
springdoc.api-docs.path=/v3/api-docs
360259
springdoc.swagger-ui.path=/swagger/index.html
260+
springdoc.api-docs.path=/docs
361261
```
362262

363-
### Testing (Local)
263+
### Testing
364264

365-
Configuration in `src/test/resources/application.properties`:
265+
Configured in `src/test/resources/application.properties`:
366266

367267
```properties
368-
# Test Database (SQLite in-memory)
369268
spring.datasource.url=jdbc:sqlite::memory:
370-
spring.datasource.driver-class-name=org.sqlite.JDBC
371-
spring.jpa.database-platform=org.hibernate.community.dialect.SQLiteDialect
372269
spring.jpa.hibernate.ddl-auto=create-drop
373270
```
374271

375-
> 💡 **Note:** Tests use SQLite in-memory database (jdbc:sqlite::memory:) for fast, isolated execution. The ISO-8601 date converter works identically with both file-based and in-memory SQLite.
272+
## Contributing
273+
274+
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on:
275+
276+
- Code of Conduct
277+
- Development workflow and best practices
278+
- Commit message conventions (Conventional Commits)
279+
- Pull request process and requirements
280+
281+
**Key guidelines:**
282+
283+
- Follow [Conventional Commits](https://www.conventionalcommits.org/) for commit messages
284+
- Ensure all tests pass (`./mvnw verify`)
285+
- Always use Maven wrapper (`./mvnw`), never system Maven
286+
- Keep changes small and focused
287+
- Review `.github/copilot-instructions.md` for architectural patterns
288+
289+
**Testing:**
290+
291+
Run the test suite with JUnit 5 + JaCoCo:
292+
293+
```bash
294+
# Run tests with coverage report
295+
./mvnw verify
296+
297+
# View coverage report
298+
open target/site/jacoco/index.html
299+
```
376300

377301
## Command Summary
378302

@@ -390,112 +314,12 @@ spring.jpa.hibernate.ddl-auto=create-drop
390314
| `docker compose down` | Stop and remove containers |
391315
| `docker compose down -v` | Stop and remove containers with volumes |
392316
| `docker compose logs -f` | View container logs |
317+
| **AI Commands** | |
318+
| `/pre-commit` | Runs linting, tests, and quality checks before committing |
319+
| `/pre-release` | Runs pre-release validation workflow |
393320

394321
> 💡 **Note:** Always use the Maven wrapper (`./mvnw`) instead of system Maven to ensure consistent builds.
395322
396-
## Releases
397-
398-
This project uses **historic football clubs** as release codenames 🏆 (inspired by Ubuntu, Android, and macOS naming conventions).
399-
400-
### Release Naming Convention
401-
402-
Releases follow the pattern: `v{SEMVER}-{CLUB}` (e.g., `v1.0.0-arsenal`)
403-
404-
- **Semantic Version**: Standard versioning (MAJOR.MINOR.PATCH)
405-
- **Club Name**: Alphabetically ordered codename from the [historic club list](CHANGELOG.md)
406-
407-
### Create a Release
408-
409-
To create a new release, follow this workflow:
410-
411-
#### 1. Create a Release Branch
412-
413-
Branch protection prevents direct pushes to `master`, so all release prep goes through a PR:
414-
415-
```bash
416-
git checkout master && git pull
417-
git checkout -b release/v1.0.0-arsenal
418-
```
419-
420-
#### 2. Update CHANGELOG.md
421-
422-
Move items from `[Unreleased]` to a new release section in [CHANGELOG.md](CHANGELOG.md), then commit and push the branch:
423-
424-
```bash
425-
# Move items from [Unreleased] to new release section
426-
# Example: [1.0.0 - Arsenal] - 2026-XX-XX
427-
git add CHANGELOG.md
428-
git commit -m "docs(changelog): prepare release notes for v1.0.0-arsenal"
429-
git push origin release/v1.0.0-arsenal
430-
```
431-
432-
#### 3. Merge the Release PR
433-
434-
Open a pull request from `release/v1.0.0-arsenal` into `master` and merge it. The tag must be created **after** the merge so it points to the correct commit on `master`.
435-
436-
#### 4. Create and Push Tag
437-
438-
After the PR is merged, pull `master` and create the annotated tag:
439-
440-
```bash
441-
git checkout master && git pull
442-
git tag -a v1.0.0-arsenal -m "Release 1.0.0 - Arsenal"
443-
git push origin v1.0.0-arsenal
444-
```
445-
446-
#### 5. Automated CD Workflow
447-
448-
This triggers the CD workflow which automatically:
449-
450-
1. Validates the club name
451-
2. Builds and tests the project with Maven
452-
3. Publishes Docker images to GitHub Container Registry with three tags
453-
4. Creates a GitHub Release with auto-generated changelog from commits
454-
455-
#### Pre-Release Checklist
456-
457-
- [ ] Release branch created from `master`
458-
- [ ] `CHANGELOG.md` updated with release notes
459-
- [ ] Changes committed and pushed on the release branch
460-
- [ ] Release PR merged into `master`
461-
- [ ] Tag created with correct format: `vX.Y.Z-club`
462-
- [ ] Club name is valid (A-Z from the [historic club list](CHANGELOG.md))
463-
- [ ] Tag pushed to trigger CD workflow
464-
465-
### Pull Docker Images
466-
467-
Each release publishes multiple tags for flexibility:
468-
469-
```bash
470-
# By semantic version (recommended for production)
471-
docker pull ghcr.io/nanotaboada/java-samples-spring-boot:1.0.0
472-
473-
# By club name (memorable alternative)
474-
docker pull ghcr.io/nanotaboada/java-samples-spring-boot:arsenal
475-
476-
# Latest release
477-
docker pull ghcr.io/nanotaboada/java-samples-spring-boot:latest
478-
```
479-
480-
> 💡 See [CHANGELOG.md](CHANGELOG.md) for the complete club list (A-Z) and release history.
481-
482-
## Contributing
483-
484-
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on:
485-
486-
- Code of Conduct
487-
- Development workflow and best practices
488-
- Commit message conventions (Conventional Commits)
489-
- Pull request process and requirements
490-
491-
**Key guidelines:**
492-
493-
- Follow [Conventional Commits](https://www.conventionalcommits.org/) for commit messages
494-
- Ensure all tests pass (`./mvnw verify`)
495-
- Always use Maven wrapper (`./mvnw`), never system Maven
496-
- Keep changes small and focused
497-
- Review `.github/copilot-instructions.md` for architectural patterns
498-
499323
## Legal
500324

501325
This project is provided for educational and demonstration purposes and may be used in production at your own discretion. All trademarks, service marks, product names, company names, and logos referenced herein are the property of their respective owners and are used solely for identification or illustrative purposes.

0 commit comments

Comments
 (0)