A small event-driven pipeline: customer reviews are submitted via REST, published to Kafka, enriched asynchronously with AI (sentiment, category, summary), and stored in Postgres.
Stack: Spring Boot · Apache Kafka · Spring AI (Gemini) · PostgreSQL · Docker
POST /reviews → saved to Postgres (PENDING) → published to Kafka
↓
Consumer picks up message
↓
Spring AI calls Gemini → {sentiment, category, summary}
↓
Saved back to Postgres (ENRICHED)
The API responds immediately (202 Accepted) while the AI enrichment happens in the background, that's the reason Kafka is in the design at all.
Prerequisites: Java 17+, Maven, Docker Desktop, a free Gemini API key
-
Configure secrets: copy the example files and fill in your own values:
cp .env.example .env cp src/main/resources/application-secrets.properties.example src/main/resources/application-secrets.properties
Fill in your Postgres credentials and Gemini API key in both files.
-
Start infrastructure:
docker compose up -d
-
Run the app:
mvn clean install mvn spring-boot:run
| Method | Endpoint | Description |
| POST | /reviews | Submit a review for enrichment |
| GET | /reviews | List all reviews |
| GET | /reviews/{id} | Get a single review |
| GET | /reviews/stats | Sentiment counts across all enriched reviews |
Request:
POST /reviews {"text": "I loved the burger at McDonald's. I was happy kid."}```
Immediate response: (202 Accepted):
{ "id": "add13edb-f92d-4713-9b2e-e15bf58c0072", "rawText": "I loved the burger at McDonald's. I was happy kid.", "sentiment": null, "category": null, "summary": null, "status": "PENDING", "createdAt": "2026-07-14T21:10:55.926691Z" }
After a couple seconds: (GET /reviews/{id}):
{ "id": "add13edb-f92d-4713-9b2e-e15bf58c0072", "rawText": "I loved the burger at McDonald's. I was happy kid.", "sentiment": "POSITIVE", "category": "product quality", "summary": "The reviewer loved the burger at McDonald's and was very happy with it.", "status": "ENRICHED", "createdAt": "2026-07-14T21:10:55.926691Z" }
Aggregate stats: (GET /reviews/stats):
{ "POSITIVE": 1, "NEGATIVE": 1, "NEUTRAL": 1 }
S-1: POST Request and Response

S-2: GET Request and check the status with the ID.

S-3: GET Request and list all the status and their count.

- Uses Gemini's free tier — expect rate limits if testing with many requests quickly.
ddl-auto: updateauto-creates the schema; not intended for production use.