A RESTful microservice for managing product catalogue data, built as part of the EAP e-commerce backend system.
- Overview
- Tech Stack
- Prerequisites
- Project Structure
- Getting Started
- Running with Docker
- Environment Variables
- API Reference
- Error Handling
- Testing
- CI/CD Pipeline
- Authors
The Product Service exposes a REST API for CRUD operations on products. It persists data to a PostgreSQL database and is designed to be consumed by other services in the EAP microservices ecosystem.
Base URL: http://localhost:8080/api/products
Interactive API docs (Swagger UI): http://localhost:8080/swagger-ui.html
OpenAPI spec: http://localhost:8080/api-docs
| Layer | Technology |
|---|---|
| Language | Java 21 |
| Framework | Spring Boot 3.5 |
| Persistence | Spring Data JPA / Hibernate |
| Database | PostgreSQL 16 |
| Validation | Jakarta Bean Validation |
| API Docs | SpringDoc OpenAPI (Swagger UI) |
| Build tool | Maven 3.9 |
| Containerisation | Docker / Docker Compose |
| Tool | Minimum version |
|---|---|
| Java (JDK) | 21 |
| Maven | 3.9 |
| Docker & Docker Compose | Docker 24 / Compose v2 |
product-service/
├── src/
│ ├── main/
│ │ ├── java/com/example/productservice/
│ │ │ ├── config/ # OpenAPI / Swagger configuration
│ │ │ ├── controller/ # REST controllers
│ │ │ ├── dto/ # Request and response DTOs
│ │ │ ├── entity/ # JPA entities
│ │ │ ├── exception/ # Custom exceptions and global handler
│ │ │ ├── mapper/ # Entity ↔ DTO mappers
│ │ │ ├── repository/ # Spring Data JPA repositories
│ │ │ └── service/ # Service interfaces and implementations
│ │ └── resources/
│ │ └── application.yml # Application configuration
│ └── test/ # Unit tests
├── .github/
│ └── workflows/
│ ├── ci.yml # Build & test on pull requests
│ └── docker-publish.yml # Build & push Docker image on merge to main
├── docker-compose.yml # Local PostgreSQL container
├── Dockerfile # Multi-stage production image
├── .env.example # Environment variable template
└── pom.xml
git clone git@github.com:CSCI44092GroupProject/product-service.git
cd product-servicecp .env.example .envEdit .env and set your database credentials if they differ from the defaults (see Environment Variables for all options).
docker compose up -dThis starts a PostgreSQL 16 container named product-db and exposes it on port 5432. A named volume (postgres_data) persists data between restarts.
mvn spring-boot:runThe service starts on http://localhost:8080. Hibernate will automatically create or update the product table on startup (ddl-auto: update).
The service ships with a multi-stage Dockerfile that produces a minimal JRE-based image.
Build the image:
docker build -t product-service:local .Run the full stack (app + database) together:
docker compose up -d # start the database
docker run --rm \
--env-file .env \
--network host \
-p 8080:8080 \
product-service:localNote: The Docker image is automatically built and pushed to Docker Hub on every merge to
mainvia the docker-publish workflow.
Copy .env.example to .env and populate the values before running the service.
| Variable | Default | Description |
|---|---|---|
SERVER_PORT |
8080 |
Port the Spring Boot application listens on |
DB_URL |
jdbc:postgresql://localhost:5432/product_db |
JDBC connection URL |
DB_USERNAME |
postgres |
Database username |
DB_PASSWORD |
postgres |
Database password |
POSTGRES_DB |
product_db |
PostgreSQL database name (used by Docker Compose) |
POSTGRES_USER |
postgres |
PostgreSQL superuser (used by Docker Compose) |
POSTGRES_PASSWORD |
postgres |
PostgreSQL password (used by Docker Compose) |
DB_PORT |
5432 |
Host port mapped to the PostgreSQL container |
Never commit
.envto version control. It is listed in.gitignore.
All endpoints are prefixed with /api/products. Request and response bodies are application/json.
GET /actuator/health
Returns the application and database health status.
Response 200 OK
{
"status": "UP",
"components": {
"db": { "status": "UP" },
"diskSpace": { "status": "UP" }
}
}POST /api/products
Request body
| Field | Type | Required | Constraints |
|---|---|---|---|
name |
string | yes | non-blank |
unitPrice |
number | yes | > 0 |
description |
string | no | — |
category |
string | no | — |
stock |
integer | yes | >= 0 |
Example request
curl -X POST http://localhost:8080/api/products \
-H "Content-Type: application/json" \
-d '{
"name": "Laptop",
"unitPrice": 999.99,
"description": "A powerful laptop",
"category": "Electronics",
"stock": 50
}'Response 201 Created
{
"productId": 1,
"name": "Laptop",
"unitPrice": 999.99,
"description": "A powerful laptop",
"category": "Electronics",
"stock": 50
}GET /api/products
Example request
curl http://localhost:8080/api/productsResponse 200 OK
[
{
"productId": 1,
"name": "Laptop",
"unitPrice": 999.99,
"description": "A powerful laptop",
"category": "Electronics",
"stock": 50
}
]Returns an empty array [] if no products exist.
GET /api/products/{id}
| Parameter | Type | Description |
|---|---|---|
id |
long | Product ID |
Example request
curl http://localhost:8080/api/products/1Response 200 OK
{
"productId": 1,
"name": "Laptop",
"unitPrice": 999.99,
"description": "A powerful laptop",
"category": "Electronics",
"stock": 50
}Response 404 Not Found — when the product does not exist.
DELETE /api/products/{id}
| Parameter | Type | Description |
|---|---|---|
id |
long | Product ID |
Example request
curl -X DELETE http://localhost:8080/api/products/1Response 204 No Content — on success (no body).
Response 404 Not Found — when the product does not exist.
All errors return a consistent JSON envelope:
{
"timestamp": "2026-06-29T10:15:30.123",
"status": 404,
"message": "Product not found with id: 99"
}Validation errors (400 Bad Request) include a field-level breakdown:
{
"timestamp": "2026-06-29T10:15:30.123",
"status": 400,
"errors": {
"name": "Product name is required",
"unitPrice": "Unit price must be greater than 0"
}
}| HTTP Status | Cause |
|---|---|
400 Bad Request |
Request body fails bean validation |
404 Not Found |
Product with the given ID does not exist |
500 Internal Server Error |
Unexpected server-side error |
Unit tests use JUnit 5, Mockito, and Spring's MockMvc.
Run all tests:
mvn testRun tests and generate a coverage report:
mvn verifyTest classes:
| Class | What it covers |
|---|---|
ProductControllerTest |
HTTP layer — request mapping, status codes, validation, error responses |
ProductServiceImplTest |
Business logic — service methods, exception throwing |
| Workflow | Trigger | What it does |
|---|---|---|
| CI | Pull request → main |
Compiles the project and runs all tests with mvn verify |
| Docker Publish | Push to main |
Builds the Docker image and pushes it to Docker Hub as <DOCKERHUB_USERNAME>/product-service:latest |
Required GitHub Secrets:
| Secret | Description |
|---|---|
DOCKERHUB_USERNAME |
Docker Hub account username |
DOCKERHUB_TOKEN |
Docker Hub access token (not your password) |
| Name | Student ID |
|---|---|
| Shehan Suraweera | CS/2020/016 |
| Pawan Perera | CS/2020/005 |