A minimal, production-ready-ish REST API built with Spring Boot 3.2.5 and Java 17. It ships with:
GET /greeting— hello endpoint- Book API (
/api/books) — CRUD with JPA + H2, validation, proper HTTP semantics - Actuator health endpoint, dev-friendly config, and tests
- Java 17
- Spring Boot 3.2.5
- Web (Spring MVC)
- Data JPA (Hibernate 6)
- Validation (Jakarta Validation)
- Actuator
- H2 (in-memory) for local/dev
- Maven
app/
├─ src/
│ ├─ main/java/com/example/demo/
│ │ ├─ DemoApplication.java
│ │ ├─ GreetingController.java
│ │ ├─ adapter/web/controller/
│ │ │ └─ BookController.java
│ │ ├─ adapter/web/dtos/
│ │ │ └─ BookDtos.java
│ │ ├─ application/portout/repository/
│ │ │ └─ BookRepository.java
│ │ ├─ application/service/
│ │ │ └─ BookService.java
│ │ └─ domain/model/
│ │ └─ Book.java
│ └─ test/java/com/example/demo/
│ ├─ adapter/web/controller/
│ │ └─ BookControllerTest.java
│ └─ GreetingControllerTest.java
├─ pom.xml
└─ README.md
- Web adapter:
adapter/web/controller/BookController - DTOs (web layer):
adapter/web/dtos/BookDtos(only used by controllers) - Application layer:
application/service/BookService— business orchestration; does not depend on web DTOs - Domain model:
domain/model/Book - Port (outbound):
application/portout/repository/BookRepository— domain-facing repository interface - Greeting feature:
GreetingControllerat the root ofcom.example.demo
Keep DTOs in the web adapter to avoid leaking HTTP shapes into the service/domain. Map to/from domain in the controller.
- Java 17+ installed and
JAVA_HOMEset - Maven or use the Maven Wrapper (recommended)
# Verify
java -version
mvn -vIf Maven isn’t installed, generate the wrapper once (requires Maven available somewhere), or use IntelliJ’s bundled Maven.
# from project root (app/)
mvn clean package
mvn spring-boot:runAlternatively, after packaging:
java -jar target/simple-java-spring-boot-project-0.0.1-SNAPSHOT.jar
# curl
curl http://localhost:8080/greeting
curl "http://localhost:8080/greeting?name=BOM"Expected
{"message":"Hello, World!"}
{"message":"Hello, BOM!"}src/main/resources/application.properties
spring.datasource.url=jdbc:h2:mem:demo;DB_CLOSE_DELAY=-1
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=true
management.endpoints.web.exposure.include=health,info- H2 console:
http://localhost:8080/h2-console(JDBC URL: shown in logs orjdbc:h2:mem:demo)
- GET
/greeting→{ "message": "Hello, World!" } - GET
/greeting?name=YourName→{ "message": "Hello, YourName!" }
Entity (Book.java)
@Entity
@Table(name = "book")
public class Book {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable=false) private String title;
@Column(nullable=false) private String author;
@Column(name="published_year") private Integer year; // avoid YEAR keyword in H2
// constructors/getters/setters
}Endpoints (controller paths prefix /api/books)
| Method | Path | Body (JSON) | Response |
|---|---|---|---|
| POST | /api/books |
{ "title":"DDD","author":"Evans","year":2003 } |
201 Created + created resource |
| GET | /api/books/{id} |
– | 200 OK + book |
| PUT | /api/books/{id} |
{ "title":"DDD 2e","author":"Evans","year":2015 } |
200 OK + updated book |
| DELETE | /api/books/{id} |
– | 204 No Content |
Example (PowerShell):
# create
irm -Method Post -ContentType 'application/json' `
-Body '{"title":"DDD","author":"Evans","year":2003}' `
http://localhost:8080/api/books
# get
irm http://localhost:8080/api/books/1
# update
irm -Method Put -ContentType 'application/json' `
-Body '{"title":"DDD 2e","author":"Evans","year":2015}' `
http://localhost:8080/api/books/1
# delete
Invoke-RestMethod -Method Delete http://localhost:8080/api/books/1Validation
- DTOs use Jakarta Validation annotations (e.g.,
@NotBlank,@Min,@Max) - Invalid input →
400 Bad Requestwith RFC7807ProblemDetail(if@RestControllerAdviceis present)
/actuator/health→ app health- Expose more endpoints via
management.endpoints.web.exposure.include
mvn -q clean testGreetingControllerTest uses @WebMvcTest + MockMvc.
# fat jar
mvn clean package
java -jar target/simple-java-spring-boot-project-0.0.1-SNAPSHOT.jar
# Build an OCI image (requires Docker)
mvn spring-boot:build-image -DskipTests
# Run it
docker run --rm -p 8080:8080 simple-java-spring-boot-project:0.0.1-SNAPSHOT- Red annotations (
@Entity,@Id, etc.) → ensurespring-boot-starter-data-jpaexists, Maven ⟳ reload, and correct importsjakarta.persistence.*. - Package errors → disable Compact Middle Packages and ensure folders are nested:
src/main/java/com/example/demo/.... - H2 DDL error about
YEAR→ we mapyearfield topublished_yearvia@Column(name="published_year"). - Duplicate dependency warning → keep only one
<dependency>forspring-boot-starter-data-jpainpom.xml. mvn/javanot found in IntelliJ terminal → setJAVA_HOME, add Maven to PATH, or use Maven Wrapper; restart IDE to pick up env vars.
- Add
BookRepository,BookService, and controller tests - Add OpenAPI:
org.springdoc:springdoc-openapi-starter-webmvc-ui→/swagger-ui.html - Switch to Postgres (prod): set
spring.datasource.url, add driverorg.postgresql:postgresql, and use Flyway for schema - Observability: add metrics, logs JSON, request tracing (Micrometer + OpenTelemetry)
MIT (or your preferred license).