Repository navigation
Implement Architecture Decision Records (ADRs) #299
Description
Problem
This project makes several opinionated architectural and technology choices (Spring Boot over Quarkus/Micronaut, Spring Data JPA + SQLite, Spring Cache, Lombok, layered architecture, stadium-themed versioning) without documenting the why behind these decisions.
Pain points:
- New contributors/learners must reverse-engineer the rationale behind technology choices
- Architectural context is lost — no record of alternatives considered or trade-offs made
- Risk of uninformed changes — future modifications may violate original design principles without understanding the constraints that shaped them
- Missed learning opportunity — as a PoC demonstrating modern Java patterns, the reasoning behind choices is as valuable as the implementation itself
- Difficult to justify forks/adaptations — others reusing this as a template can't make informed decisions about what to keep vs. change
This is especially problematic for a learning-focused PoC where understanding the "why" is critical for educational value.
Proposed Solution
Implement Architecture Decision Records (ADRs) using the Michael Nygard template to systematically document all architecturally significant decisions.
Benefits:
- Better onboarding — new contributors understand context immediately
- Enhanced learning value — learners see not just what was built, but why
- Informed evolution — future changes can reference past decisions and their constraints
- Knowledge preservation — rationale survives team/contributor changes
- Best practice demonstration — shows proper architecture documentation for production systems
- Easier forking — others can adapt this PoC with full context of original decisions
Suggested Approach
1. Directory Structure
docs/
adr/
README.md # Index and overview
0001-adopt-spring-boot.md
0002-spring-data-jpa-sqlite.md
0003-spring-cache-memory.md
0004-layered-architecture.md
0005-lombok-boilerplate-reduction.md
0006-springdoc-openapi.md
0007-docker-single-container.md
0008-stadium-themed-versioning.md
0009-uuid-surrogate-squad-number-natural-key.md
0010-full-replace-put-no-patch.md
0011-mixed-test-strategy.md
0012-no-flyway-manual-ddl-dml-scripts.md
template.md # ADR template for future decisions
2. ADR Template
Use the Michael Nygard format:
# ADR-NNNN: {Title}
Date: YYYY-MM-DD
## Status
{Proposed | Accepted | Deprecated | Superseded by ADR-XXXX}
## Context
{Describe the forces at play: technical, business, team constraints.
What problem needs solving? What alternatives exist?}
## Decision
{State the decision in full sentences with active voice: "We will..."}
## Consequences
{Describe the resulting context after applying this decision:
- Positive outcomes
- Negative trade-offs
- Neutral implications
All affect the project going forward.}3. Initial ADRs to Create (Retroactive)
ADRs are numbered in the order decisions were made (or, for retroactive records, the order that best reflects foundational-to-incidental progression). Each record is fully self-contained — no ADR depends on another.
ADRs 0001–0008 document existing, already-implemented decisions (status: Accepted):
-
ADR-0001: Adopt Spring Boot as REST API Framework
- Context: Need a production-grade Java framework for a CRUD REST API
- Alternatives: Quarkus, Micronaut, Jakarta EE + Jersey, plain Spring MVC
- Decision: Spring Boot 4.0.0
- Why: Largest ecosystem, auto-configuration, embedded server, wide industry adoption, cross-language comparison favours the dominant framework per ecosystem
-
ADR-0002: Use Spring Data JPA with SQLite
- Context: Need persistence with ORM support and multi-database compatibility
- Alternatives: Plain JDBC, MyBatis, jOOQ, PostgreSQL as primary
- Decision: Spring Data JPA + Hibernate + SQLite (file-based runtime, in-memory for tests)
- Why: Demonstrates JPA patterns, SQLite requires no external service, in-memory variant gives fast isolated tests, community Hibernate dialect bridges SQLite gap
-
ADR-0003: Implement In-Memory Caching with Spring Cache
- Context: Need caching for GET endpoints without external infrastructure
- Alternatives: Redis, Caffeine, Hazelcast, no caching
- Decision: Spring
@Cacheablewith simple in-memory provider (no expiry) - Why: Zero external dependencies, built into Spring Boot, sufficient for PoC, demonstrates the pattern
-
ADR-0004: Adopt Layered Architecture (Controller → Service → Repository → JPA)
- Context: Need clear separation of concerns across HTTP, business, and data layers
- Alternatives: Hexagonal/ports-and-adapters, CQRS, flat structure
- Decision: Strict 3-layer: controllers delegate to services, services own business logic and transactions, repositories handle data access
- Why: Industry-standard Spring idiom, highly testable per layer, appropriate complexity for PoC, matches patterns learners will encounter in real projects
-
ADR-0005: Use Lombok to Reduce Boilerplate
- Context: Java verbosity in entity/DTO classes with getters, setters, constructors, and builders
- Alternatives: Java Records, manual boilerplate, MapStruct only
- Decision: Lombok (
@Data,@Builder,@RequiredArgsConstructor,@AllArgsConstructor) - Why: Dramatically reduces noise, constructor injection via
@RequiredArgsConstructorenforces good DI habits, widely adopted in enterprise Spring projects
-
ADR-0006: Use SpringDoc OpenAPI 3 for API Documentation
- Context: Need interactive, standards-compliant API documentation
- Alternatives: springfox (unmaintained), hand-written OpenAPI YAML, no docs
- Decision: SpringDoc OpenAPI 3 with Swagger UI
- Why: Actively maintained, Spring Boot 3+ compatible, auto-generates from annotations, interactive UI at
/swagger-ui.html
-
ADR-0007: Single-Container Docker Deployment
- Context: Need containerisation strategy for the API
- Alternatives: Docker Compose multi-service, Kubernetes, no containers
- Decision: Single container with a bind-mounted volume for the SQLite file
- Why: Simplicity, appropriate for PoC, demonstrates Docker patterns without orchestration overhead
-
ADR-0008: Use Stadium-Themed Semantic Versioning
- Context: Need a memorable release naming convention consistent with the cross-language comparison series
- Alternatives: Standard semver only, animal names, city names
- Decision: Famous football stadiums A–Z
- Why: Fun, memorable, culturally universal for a football-themed domain, alphabetical progression makes ordering obvious
ADRs 0009–0012 document forward-looking or in-flight decisions. They start as Proposed and are updated to Accepted once the corresponding issue is implemented:
-
ADR-0009: Demote UUID to Surrogate Key and Promote Squad Number to Natural Key (tracks Migrate Player PK from Long to UUID #268)
- Context: The
Playerentity uses a sequentialLongPK — predictable, not globally unique, and semantically wrong. Squad numbers are the stable, domain-meaningful identifier for players within a team. UUID provides global uniqueness without the drawbacks of sequential IDs, but should not be the mutation key because it is opaque to API consumers. - Alternatives: Keep
LongPK, use UUID as PK (exposed in all endpoints), composite key - Decision: UUID becomes a non-PK surrogate (unique, non-updatable, generated at application level via
@PrePersist).squadNumberis promoted to@Id.PUT /players/{squadNumber}andDELETE /players/{squadNumber}use squad number as path variable.GET /players/{id}(UUID) is retained for admin/internal lookup only. - Why: Squad numbers are natural keys — unique per team, stable, and meaningful to consumers. UUID provides global uniqueness for distributed scenarios without exposing sequential counters. Keeping UUID accessible via a dedicated admin endpoint preserves internal traceability without burdening the public API.
- Context: The
-
ADR-0010: Full-Replace PUT, No PATCH (status:
Accepted)- Context: HTTP defines PUT (full replacement) and PATCH (partial update). Both are common in REST APIs. The choice affects the DTO design (all fields required vs optional), the service layer logic (overwrite all vs merge), and the client contract.
- Alternatives: PATCH only, both PUT and PATCH, PUT with optional fields
- Decision: PUT performs a full replacement — the request body represents the complete new state of the resource. No PATCH endpoint is provided.
- Why: Simpler to implement correctly; no merge logic, no ambiguity about absent fields, no need to choose a patch format (JSON Merge Patch vs JSON Patch). For a PoC with a small, flat domain model, requiring the full resource on update is not a meaningful burden for clients. Consistent with the same decision made across the Python and Go sibling projects.
-
ADR-0011: Mixed Test Strategy (MockMvc Unit Tests + In-Memory SQLite Integration Tests) (status:
Accepted)- Context: Testing strategies range from fully mocked unit tests (fast, isolated, risk of mock divergence) to fully integrated tests against a real database (high confidence, slower). Two distinct scenario categories exist: happy paths and expected error branches (400, 404, 409) that can be exercised with a real database, and infrastructure-level error branches (500s from service or repository failures) that require controlled failure injection.
- Alternatives: Unit tests with mocks only, integration tests only, TestContainers with PostgreSQL
- Decision: Use MockMvc + Mockito for controller and service unit tests — including 500-level error branches unreachable with a healthy database. Use in-memory SQLite (auto-cleared after each test) for integration tests covering the full request/response cycle. The boundary is explicit: if a scenario can be triggered with a real database, it uses the real database.
- Why: In-memory SQLite gives near-zero setup cost with real constraint enforcement. Mockito covers error branches that cannot be triggered otherwise. Consistent with Go's mixed strategy. Avoids the false confidence documented in the TypeScript sibling project, where mocked Sequelize tests passed while real queries failed.
-
ADR-0012: No Flyway — Manual DDL/DML Scripts (tracks Implement Flyway for database migrations #130)
- Context: Spring Boot projects typically use Flyway (Apache 2.0) or Liquibase for versioned schema migrations. The alternatives considered: Flyway (versioned, reversible migrations, strong Spring Boot integration), Liquibase (XML/YAML/SQL changelogs, broader database support), manual
ddl.sql/dml.sqlscripts executed at startup or test setup with no migration history. - Alternatives: Flyway, Liquibase, Spring Boot
spring.sql.initwith manual scripts (current) - Decision: Use manual
ddl.sql(schema) anddml.sql(seed data) scripts for now. Migration to Flyway is tracked in issue Implement Flyway for database migrations #130 and will be revisited alongside the PostgreSQL support work in issue Add PostgreSQL support as configurable database provider #286. - Why: For a PoC with a stable schema and a single committed SQLite file, Flyway adds configuration overhead without meaningful benefit today. When PostgreSQL support is introduced (Add PostgreSQL support as configurable database provider #286), versioned migrations become necessary — at that point Flyway is the natural choice given its first-class Spring Boot auto-configuration and Apache 2.0 license.
- Context: Spring Boot projects typically use Flyway (Apache 2.0) or Liquibase for versioned schema migrations. The alternatives considered: Flyway (versioned, reversible migrations, strong Spring Boot integration), Liquibase (XML/YAML/SQL changelogs, broader database support), manual
4. Integration with Existing Docs
- Update
README.md— Add "Architecture Decisions" section linking todocs/adr/ - Update
.github/copilot-instructions.md— Reference ADRs in an "Additional Resources" section - Add guideline — New architecturally significant decisions must include an ADR (note in contributing guidance)
5. File Modifications Required
Create docs/adr/README.md
# Architecture Decision Records (ADR)
This directory contains records of architecturally significant decisions made in this project.
## Index
| ADR | Title | Status |
|-----|-------|--------|
| [ADR-0001](0001-adopt-spring-boot.md) | Adopt Spring Boot as REST API Framework | Accepted |
| [ADR-0002](0002-spring-data-jpa-sqlite.md) | Use Spring Data JPA with SQLite | Accepted |
| [ADR-0003](0003-spring-cache-memory.md) | Implement In-Memory Caching with Spring Cache | Accepted |
| [ADR-0004](0004-layered-architecture.md) | Adopt Layered Architecture | Accepted |
| [ADR-0005](0005-lombok-boilerplate-reduction.md) | Use Lombok to Reduce Boilerplate | Accepted |
| [ADR-0006](0006-springdoc-openapi.md) | Use SpringDoc OpenAPI 3 for API Documentation | Accepted |
| [ADR-0007](0007-docker-single-container.md) | Single-Container Docker Deployment | Accepted |
| [ADR-0008](0008-stadium-themed-versioning.md) | Use Stadium-Themed Versioning | Accepted |
| [ADR-0009](0009-uuid-surrogate-squad-number-natural-key.md) | Demote UUID to Surrogate Key and Promote Squad Number to Natural Key | Proposed |
| [ADR-0010](0010-full-replace-put-no-patch.md) | Full-Replace PUT, No PATCH | Accepted |
| [ADR-0011](0011-mixed-test-strategy.md) | Mixed Test Strategy | Accepted |
| [ADR-0012](0012-no-flyway-manual-ddl-dml-scripts.md) | No Flyway — Manual DDL/DML Scripts | Proposed |
## Resources
- [Michael Nygard's ADR article](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions)
- [ADR GitHub organization](https://adr.github.io/)
- [Microsoft Azure Well-Architected: ADRs](https://learn.microsoft.com/en-us/azure/well-architected/architect-role/architecture-decision-record)Acceptance Criteria
- Directory structure created:
docs/adr/withREADME.mdandtemplate.md - ADRs 0001–0008, 0010–0011 written with status
Acceptedusing Michael Nygard template format - ADR-0009 written with status
Proposed, referencing issue Migrate Player PK from Long to UUID #268; updated toAcceptedonce Migrate Player PK from Long to UUID #268 is implemented - ADR-0012 written with status
Proposed, referencing issue Implement Flyway for database migrations #130; updated toAcceptedonce Implement Flyway for database migrations #130 is implemented - README.md updated with "Architecture Decisions" section linking to
docs/adr/ -
.github/copilot-instructions.mdupdated with reference to ADRs in an "Additional Resources" section - ADRs are discoverable: reachable via README, copilot instructions, or file explorer
- ADRs are educational: each ADR clearly explains alternatives considered and trade-offs made
- Template provided:
docs/adr/template.mdexists for future ADRs - CHANGELOG.md updated: entry in
[Unreleased]section under "Added"
References
ADR Resources
- Documenting Architecture Decisions — Michael Nygard (2011) — Original blog post that popularised ADRs
- ADR GitHub Organization — Templates, tools, and best practices
- Microsoft Azure Well-Architected: ADRs — Enterprise perspective on maintaining ADRs
- Michael Nygard ADR Template — Canonical template format
Tooling (Optional)
- adr-tools — Command-line tools for managing ADRs
- log4brains — Modern ADR toolchain with architecture knowledge base
Related Issues
- Inspired by nanotaboada/python-samples-fastapi-restful#482
- ADR-0009 tracks the decision described in Migrate Player PK from Long to UUID #268
- ADR-0012 tracks the decision described in Implement Flyway for database migrations #130; will be revisited alongside PostgreSQL support (Add PostgreSQL support as configurable database provider #286)
Note: This feature aligns with the project's philosophy of being a learning-focused PoC by making architectural reasoning explicit and accessible to learners and contributors.
Activity
coderabbitai commentedon Apr 6, 2026
🔗 Related PRs
#234 - feat: upgrade to Spring Boot 4.0 and JDK 25 LTS [merged]
#235 - feat: migrate database from H2 to SQLite [merged]
#265 - feat(domain)!: refactor from Books to Players domain model (
#248) [merged]#266 - test: adopt BDD Given-When-Then pattern across all tests [merged]
#297 - docs: normalize README structure and add RELEASES.md [merged]
📝 Issue Planner
Check the box below or use the
@coderabbitai plancommand to generate an implementation plan and prompts that you can use with your favorite coding assistant.✅ Successfully generated implementation plan.
💡 Use
@coderabbitai <feedback>to replan🧪 Issue enrichment is currently in open beta.
You can configure auto-planning by selecting labels in the issue_enrichment configuration.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:💬 Have feedback or questions? Drop into our discord!
Akkii50 commentedon Apr 12, 2026
Hi, I’d like to work on implementing ADRs for this project.
I’m planning to start by documenting decisions around Spring Boot, JPA, and SQLite.
Thanks!
coderabbitai commentedon Jun 7, 2026
Coding Plan
Summary
docs/adr/directory with a reusable template and comprehensive index document linking to all ADRsDesign Choices
Design Choice 1: ADR status for 0009 and 0012
Options Considered:
Chosen Option: 2
Rationale: The exploration confirms Flyway was added in v2.0.1-chelsea and UUID/squadNumber changes shipped in v2.0.0-barcelona. ADR-0009 should be "Accepted" and ADR-0012's title and status should reflect that Flyway was adopted (reversing the "No Flyway" decision), making it either "Superseded" or rewritten as "ADR-0012: Adopt Flyway for Database Migrations" with status "Accepted".
💡 User Tips
Regenerate the plan with different choices with
@coderabbitai <feedback>.Implementation Steps
Phase 1: ADR Infrastructure Setup
Create the foundational directory structure, template file, and index document that will house all ADRs.
Task 1: Create ADR Directory and Template
Establish the
docs/adr/directory structure and provide a reusable template for future ADRs.docs/adr/directory at repository rootdocs/adr/template.mdfollowing Michael Nygard format with placeholders for Title, Date, Status, Context, Decision, and Consequences sectionsTask 2: Create ADR Index Document
Create the README.md index that provides navigation and context for all ADRs.
docs/adr/README.mdwith introductory explanation of what ADRs are and their purpose🤖 Prompt for AI agents
Phase 2: ADR Content Creation
Write all 12 ADR documents covering the project's architectural decisions, from foundational framework choices to specific implementation strategies.
Task 1: Core Framework ADRs (0001-0003)
Document the foundational technology stack decisions.
docs/adr/0001-adopt-spring-boot.md(Accepted): Document choice of Spring Boot 4.0 over Quarkus, Micronaut, Jakarta EE. Emphasize ecosystem size, auto-configuration, embedded server, and cross-language comparison rationaledocs/adr/0002-spring-data-jpa-sqlite.md(Accepted): Document JPA + Hibernate + SQLite choice over plain JDBC, MyBatis, jOOQ. Highlight file-based runtime simplicity, in-memory testing, and community Hibernate dialectdocs/adr/0003-spring-cache-memory.md(Accepted): Document simple in-memory cache (ConcurrentHashMap) over Redis, Caffeine. Note zero external dependencies and PoC appropriatenessTask 2: Architecture and Tooling ADRs (0004-0006)
Document structural patterns and developer experience tooling decisions.
docs/adr/0004-layered-architecture.md(Accepted): Document strict 3-layer separation over hexagonal/CQRS. Emphasize industry-standard Spring idiom and per-layer testabilitydocs/adr/0005-lombok-boilerplate-reduction.md(Accepted): Document Lombok over Java Records or manual code. Cover@Data,@Builder,@RequiredArgsConstructorpatternsdocs/adr/0006-springdoc-openapi.md(Accepted): Document SpringDoc OpenAPI 3 over springfox (unmaintained) or hand-written specs. Note Spring Boot 3+ compatibility and interactive UITask 3: Deployment and Versioning ADRs (0007-0008)
Document containerization and release naming decisions.
docs/adr/0007-docker-single-container.md(Accepted): Document single container with volume mount over multi-service compose or Kubernetes. Emphasize PoC simplicitydocs/adr/0008-stadium-themed-versioning.md(Accepted): Document football club A-Z naming convention. Note cross-language series consistency and the memorable, culturally universal nature for a football domainTask 4: Data Model ADRs (0009-0010)
Document entity design and HTTP method decisions.
docs/adr/0009-uuid-surrogate-squad-number-natural-key.md(Accepted): Document UUID as surrogate key with squadNumber as natural key for mutations. Reference that this was implemented in v2.0.0-barcelonadocs/adr/0010-full-replace-put-no-patch.md(Accepted): Document PUT-only approach over PATCH or both. Highlight simplicity, no merge logic ambiguity, and consistency with Python/Go sibling projectsTask 5: Testing and Migration ADRs (0011-0012)
Document testing strategy and database migration decisions.
docs/adr/0011-mixed-test-strategy.md(Accepted): Document MockMvc + Mockito for unit/error tests combined with in-memory SQLite for integration tests. Explain boundary between mocked and real-DB scenariosdocs/adr/0012-adopt-flyway-migrations.md(Accepted): Document adoption of Flyway for versioned migrations (supersedes initial "manual scripts" approach). Reference that this was implemented in v2.0.1-chelsea. Note the migration path from manual ddl.sql/dml.sql to Flyway V1→V3 versioned scripts🤖 Prompt for AI agents
Phase 3: Documentation Integration
Update existing documentation files to reference the new ADR system and ensure discoverability.
Task 1: Update README.md
Add Architecture Decisions section to the main README.
## Architecture Decisionssection after the existing## Architecturesection (around line 137)docs/adr/README.mdfor the full indexTask 2: Update Copilot Instructions
Reference ADRs in the AI assistant guidance document.
.github/copilot-instructions.mdto add a new## Additional Resourcessection (or append to existing appropriate section)docs/adr/explaining that architectural decisions are documented thereTask 3: Update CHANGELOG.md
Document this feature in the unreleased changes section.
[Unreleased]section in the "Added" subsectiondocs/adr/"🤖 Prompt for AI agents
Research
This is a Spring Boot 4.0 REST API proof-of-concept for managing FIFA World Cup player data. Documentation currently lives at the repo root (README.md, CHANGELOG.md, CONTRIBUTING.md) with no
docs/directory. The project follows a strict layered architecture (Controller → Service → Repository → JPA), uses SQLite with Spring Data JPA, Spring Cache with ConcurrentHashMap, Lombok for boilerplate reduction, and SpringDoc OpenAPI for documentation. Releases follow a football club-themed semantic versioning convention (A–Z clubs). The CHANGELOG follows Keep a Changelog format.🚀 Next Steps
🤖 All AI agent prompts combined
💡 Iterate on the plan with:
`@coderabbitai` <feedback>💬 Have feedback or questions? Drop into our discord!
Merge pull request #338 from nanotaboada/docs/299-architecture-decisi…
docs(adr): implement Architecture Decision Records (nanotaboada#299)
chore: resolve merge conflict in CHANGELOG.md with master