REST API for a tool and equipment rental platform. Demonstrates Clean Architecture (Onion pattern, Robert C. Martin): strict inward dependency rule, framework-free domain layer, and a pricing engine testable with plain JUnit 5 and no Spring context.
flowchart TD
subgraph ADAPTER["ADAPTER"]
style ADAPTER fill:#dbeafe,stroke:#3b82f6
A1["ToolController"]
A2["RentalController"]
A3["AuthenticationController"]
A4["ReviewController / CommentController"]
A5["CategoryController"]
A6["ModerationController"]
end
subgraph APPLICATION["APPLICATION"]
style APPLICATION fill:#dcfce7,stroke:#22c55e
B1["ToolService"]
B2["RentalService"]
B3["AuthenticationService / JwtService"]
B4["ReviewService / CommentService"]
B5["CategoryService"]
end
subgraph DOMAIN["DOMAIN"]
style DOMAIN fill:#fef9c3,stroke:#eab308
C1["RentalPriceCalculator"]
C2["Price"]
C3["PricingGrid / PricingTier"]
C4["RentalDuration"]
end
A1 --> B1
A2 --> B2
A3 --> B3
A4 --> B4
A5 --> B5
B2 --> C1
C1 --> C2
C1 --> C3
C1 --> C4
Three concentric layers: ADAPTER knows APPLICATION, APPLICATION knows DOMAIN, DOMAIN knows nothing outside itself. The pricing engine lives entirely in DOMAIN and runs under plain JUnit 5 with no Spring context or database.
| Layer | Package | Responsibilities |
|---|---|---|
| ADAPTER | adapter/controller/ adapter/persistence/ adapter/dto/ |
Spring REST controllers, JPA entity mappings, request/response DTOs, GlobalExceptionHandler |
| APPLICATION | application/service/ application/exception/ |
Use-case services, JWT authentication, custom exceptions |
| DOMAIN | domain/pricing/ |
Entities, Value Objects, pricing rules. Zero Spring or JPA imports. |
Business rules belong in the domain, not in @NotNull annotations on a DTO:
// DOMAIN layer: zero Spring/JPA dependency
public record Price(float value) {
public Price {
if (value < 0) throw new IllegalArgumentException("Price cannot be negative");
}
public Price plus(Price other) { return new Price(value + other.value); }
public Price multipliedBy(float factor) { return new Price(value * factor); }
}Context. A REST API that mixes JPA entities, business rules, and HTTP concerns in the same class becomes hard to test and fragile to evolve.
Decision.
Three concentric layers with a hard dependency rule: domain/ imports only the Java standard library. application/ imports domain/. adapter/ imports both, but inner layers never import outer ones.
Consequence. The pricing engine is fully testable with plain JUnit 5. Adding a new delivery channel (CLI, messaging) requires only a new adapter: no business logic changes needed.
Context. Validation logic scattered across service methods and DTO annotations leaves the domain object unprotected when instantiated directly in tests or other services.
Decision.
Price uses a Java record canonical constructor to enforce invariants at construction time, not at the service layer.
Consequence.
An invalid Price cannot exist anywhere in the codebase. Tests that construct domain objects directly receive the same invariant guarantees as production code.
Prerequisites: JDK 21, Maven 3.9+, MariaDB running on port 3306 with database shelton_db.
git clone https://github.com/<your-handle>/GoodToolers.git
cd GoodToolers
mvn spring-boot:runAPI available at http://localhost:8080/api/v1.
| Concern | Technology |
|---|---|
| Language | Java 21 |
| Framework | Spring Boot 3.2.5 |
| Security | Spring Security + jjwt 0.12.3 |
| Persistence | Spring Data JPA + MariaDB |
| Build | Maven |
| Testing | JUnit Jupiter 5.11.2 + AssertJ 3.26.3 |
Domain tests run without Spring context:
# All tests
mvn test
# Single class
mvn test -Dtest=RentalPriceCalculatorTestCoverage targets domain/pricing/: RentalPriceCalculatorTest and RentalDurationTest cover tier-selection logic and duration-rounding rules.
Full endpoint reference with request/response examples: API_REFERENCE.md
Base path: http://localhost:8080/api/v1
| Resource | Base path |
|---|---|
| Authentication | /api/v1/auth |
| Tools | /api/v1/tools |
| Categories | /api/v1/categories |
| Rentals | /api/v1/rentals |
| Reviews | /api/v1/reviews |
| Comments | /api/v1/comments |
| Moderation | /api/v1/moderation |