Skip to content

Latest commit

Β 

History

20 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Track Day Legends API 🏁

Welcome to the Track Day Legends API! This is an elegant, high-performance, and robust REST API designed for petrolheads, track day enthusiasts, and amateur racing drivers. It manages a technical catalog of iconic sports cars (CarModel) and their mechanical variants or specifications (EngineSpec).

πŸ—οΈ Architectural Design

The project is built following strict Hexagonal Architecture (Ports and Adapters) and Domain-Driven Design (DDD) principles to ensure maximum maintainability, scalability, and testability:

  1. Pure Domain (domain): Houses the core business entities (CarModel and EngineSpec) completely decoupled from Spring, JPA, Jackson, or other infrastructure frameworks.
  2. Application (application): Defines use cases (inbound ports in and outbound ports out) and implements business logic as pure Java POJOs.
  3. Persistence Adapter (adapter.outbound.persistence): Interacts with Spring Data JPA over an H2 in-memory database, utilizing explicit manual mappers to prevent leaking JPA entities into the domain.
  4. Web Adapter (adapter.inbound.web): Implements clean REST controllers, declarative request validation DTOs, manual mappers, and a centralized GlobalExceptionHandler.
  5. Infrastructure (infrastructure): Configures manual Spring Beans via BeanConfiguration and loads rich, realistic demo data with DemoDataLoader.

Design Note: Lombok and MapStruct are intentionally omitted in favor of explicit, standard, and highly readable Java 21 code.


πŸ—‚οΈ Directory Tree

Track-Day-Legends-API/
β”œβ”€β”€ pom.xml
β”œβ”€β”€ README.md
└── src/
    β”œβ”€β”€ main/
    β”‚   β”œβ”€β”€ java/
    β”‚   β”‚   └── com/
    β”‚   β”‚       └── example/
    β”‚   β”‚           └── trackdaylegends/
    β”‚   β”‚               β”œβ”€β”€ TrackDayLegendsApplication.java
    β”‚   β”‚               β”œβ”€β”€ domain/
    β”‚   β”‚               β”‚   β”œβ”€β”€ exception/ (Domain exceptions)
    β”‚   β”‚               β”‚   └── model/ (Pure models: CarModel, EngineSpec)
    β”‚   β”‚               β”œβ”€β”€ application/
    β”‚   β”‚               β”‚   β”œβ”€β”€ port/ (Inbound & Outbound ports)
    β”‚   β”‚               β”‚   └── usecase/ (Pure Java service/use case implementations)
    β”‚   β”‚               β”œβ”€β”€ adapter/
    β”‚   β”‚               β”‚   β”œβ”€β”€ inbound/web/ (REST controllers, DTOs, mappers, handlers)
    β”‚   β”‚               β”‚   └── outbound/persistence/ (JPA Entities, repositories, adapters)
    β”‚   β”‚               └── infrastructure/
    β”‚   β”‚                   β”œβ”€β”€ config/ (Manual Spring Bean wiring)
    β”‚   β”‚                   └── bootstrap/ (Initial demo data loader)
    β”‚   └── resources/
    β”‚       └── application.properties
    └── test/ (Unit & Integration tests using JUnit 5 + Mockito)

πŸš€ How to Run the Project

Prerequisites:

  • Java 21 or higher installed.
  • Maven 3.8+ installed.

Build and Run:

  1. Navigate to the root directory of the project:
    cd Track-Day-Legends-API
  2. Build the project and run the tests:
    mvn clean test
  3. Run the Spring Boot application:
    mvn spring-boot:run
  4. The server will start on port 8080.
  5. Access the H2 Database Web Console at:

πŸ”Œ API Endpoints

πŸš— Car Model Management (CarModel)

Method Endpoint Description Optional Filter Parameters
GET /api/car-models List all car models brand, year, segment, active
GET /api/car-models/{id} Retrieve a model with all its engine specs
POST /api/car-models Create a new car model (JSON Payload)
PUT /api/car-models/{id} Update an existing car model (JSON Payload)
PATCH /api/car-models/{id}/deactivate Deactivate a car model and its specs
DELETE /api/car-models/{id} Delete a car model

βš™οΈ Engine Specification Management (EngineSpec)

Method Endpoint Description Optional Filter Parameters
GET /api/engine-specs List all engine specifications engineType, minHorsepower, maxZeroToHundred, drivetrain, active
GET /api/engine-specs/{id} Retrieve an engine spec by ID
POST /api/car-models/{carModelId}/engine-specs Create an engine spec for a specific car model (JSON Payload)
PUT /api/engine-specs/{id} Update an engine specification (JSON Payload)
PATCH /api/engine-specs/{id}/deactivate Deactivate an engine specification
DELETE /api/engine-specs/{id} Delete an engine specification

πŸ“ˆ Metrics and Analytics

Method Endpoint Description
GET /api/engine-specs/search/fastest Retrieve top 5 fastest engine specs by 0-100 km/h acceleration
GET /api/engine-specs/search/most-powerful Retrieve top 5 most powerful engine specs by horsepower
GET /api/stats/summary Global database dashboard statistics (totals, averages, fastest 0-100, etc.)
GET /api/health/demo Quick API diagnostic health check

πŸ’‘ Practical curl Request Examples

1. List car models (filtering by brand "Toyota")

curl -X GET "http://localhost:8080/api/car-models?brand=Toyota" -H "Accept: application/json"

2. Get a car model with all its associated engine specs (ID 1: Toyota GR Yaris)

curl -X GET "http://localhost:8080/api/car-models/1" -H "Accept: application/json"

3. Create a new car model (Alpine A110 R)

curl -X POST "http://localhost:8080/api/car-models" \
  -H "Content-Type: application/json" \
  -d '{
    "brand": "Alpine",
    "model": "A110 R",
    "year": 2024,
    "segment": "S",
    "bodyStyle": "Coupe",
    "country": "France",
    "description": "The hardcore, 100% track-focused edition of the legendary Alpine A110, extensively utilizing carbon fiber to shed weight."
  }'

4. Create a performance engine specification for the model above (e.g., model ID 9)

curl -X POST "http://localhost:8080/api/car-models/9/engine-specs" \
  -H "Content-Type: application/json" \
  -d '{
    "versionName": "A110 R Turini",
    "engineType": "Turbo Petrol",
    "engineConfiguration": "1.8L L4",
    "displacementCc": 1798,
    "horsepowerHp": 300,
    "torqueNm": 340,
    "transmission": "7-speed dual-clutch EDC",
    "drivetrain": "RWD",
    "zeroToHundredSeconds": 3.9,
    "topSpeedKph": 285,
    "fuelConsumptionL100": 7.0
  }'

5. Retrieve top 5 most powerful engine specs in the database

curl -X GET "http://localhost:8080/api/engine-specs/search/most-powerful" -H "Accept: application/json"

6. Get global metrics and statistics

curl -X GET "http://localhost:8080/api/stats/summary" -H "Accept: application/json"

7. Trigger validation rules by posting an invalid payload (blank brand, invalid year)

curl -X POST "http://localhost:8080/api/car-models" \
  -H "Content-Type: application/json" \
  -d '{
    "brand": "",
    "model": "Invalid",
    "year": 1945,
    "segment": "C"
  }'

Expected 400 Bad Request Response:

{
  "timestamp": "2026-07-07T22:22:38.123456",
  "status": 400,
  "error": "Bad Request",
  "message": "Validation error in request payload",
  "path": "/api/car-models",
  "validationErrors": {
    "brand": "Brand cannot be empty",
    "year": "Year must be at least 1950"
  }
}

Enjoy burning virtual rubber with the Track Day Legends API! πŸŽοΈπŸ’¨


🐳 Docker + ☸️ Kubernetes (Helm)

Build Docker image (multi-stage)

docker build -t ghcr.io/robertol18/track-day-legends-api:1.0.0 .

Push image (example with GHCR)

docker push ghcr.io/robertol18/track-day-legends-api:1.0.0

Local files for private registry access

  • helm/track-day-legends/values-private.yaml is a local-only override file ignored by Git.
  • helm/track-day-legends/create-ghcr-secret.local.sh is a local-only script ignored by Git.

Create the GHCR pull secret in Kubernetes

Update GHCR_TOKEN in helm/track-day-legends/create-ghcr-secret.local.sh and run the script for the target namespace. The existing ghcr-pull secret will be created or updated in place.

chmod +x ./helm/track-day-legends/create-ghcr-secret.local.sh
./helm/track-day-legends/create-ghcr-secret.local.sh track-day-legends-dev

Install/upgrade on Kubernetes with Helm (dev)

helm upgrade --install track-day-legends ./helm/track-day-legends \
  -f ./helm/track-day-legends/values.yaml \
  -f ./helm/track-day-legends/values-dev.yaml \
  -f ./helm/track-day-legends/values-private.yaml \
  --namespace track-day-legends-dev \
  --create-namespace

Install/upgrade on Kubernetes with Helm (tst)

./helm/track-day-legends/create-ghcr-secret.local.sh track-day-legends-tst

helm upgrade --install track-day-legends ./helm/track-day-legends \
  -f ./helm/track-day-legends/values.yaml \
  -f ./helm/track-day-legends/values-tst.yaml \
  -f ./helm/track-day-legends/values-private.yaml \
  --namespace track-day-legends-tst \
  --create-namespace

Install/upgrade Observability Stack with Helm (dev)

helm dependency build ./helm/observability

helm upgrade --install track-day-observability ./helm/observability \
  -f ./helm/observability/values.yaml \
  -f ./helm/observability/values-dev.yaml \
  --namespace track-day-legends-dev \
  --create-namespace

Install/upgrade Observability Stack with Helm (tst)

helm dependency build ./helm/observability

helm upgrade --install track-day-observability ./helm/observability \
  -f ./helm/observability/values.yaml \
  -f ./helm/observability/values-tst.yaml \
  --namespace track-day-legends-tst \
  --create-namespace

Observability endpoints and signal flow

  • OpenTelemetry endpoint consumed by the API chart: http://otel-collector:4318
  • Metrics pipeline:
    • App metrics (/actuator/prometheus) scraped by Prometheus
    • OTEL metrics exported to Mimir (/api/v1/push)
  • Logs pipeline:
    • OTEL logs exported by Collector to Loki (/otlp)
  • Traces pipeline:
    • OTEL traces exported by Collector to Tempo (OTLP gRPC :4317)
  • Grafana preprovisions datasources for Prometheus, Mimir, Loki, and Tempo.

Security notes

  • Do not store real secrets in values*.yaml.
  • Do not store real registry credentials in Git; keep them only in create-ghcr-secret.local.sh, which is ignored by Git.
  • If a GHCR token is exposed, revoke it in GitHub, replace GHCR_TOKEN in the local script, and run it again.
  • Use secret.existingSecret (or External Secrets/Sealed Secrets) in tst.
  • Kubernetes probes are mapped to Spring Boot Actuator endpoints:
    • /actuator/health/liveness
    • /actuator/health/readiness

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages