Microservices-based e-commerce platform with a Spring Boot backend and Angular frontend.
Backend services:
discovery-service(Eureka service registry)gateway-service(single entrypoint, JWT auth, routing)user-service(auth + profile)product-service(catalog management)media-service(image upload + media metadata)
Supporting infrastructure:
- MongoDB
- MinIO (object storage)
- Kafka (product deletion events and asynchronous media cleanup)
Frontend:
- Angular app consuming gateway APIs
backend/discovery-service/README.mdbackend/gateway-service/README.mdbackend/user-service/README.mdbackend/product-service/README.mdbackend/media-service/README.mddocs/backend-unit-testing-guide.md(JUnit + backend testing walkthrough)frontend/README.mddocs/frontend-unit-testing-guide.md(Jasmine/Karma frontend testing walkthrough)docs/jenkins-guide.md(Jenkins setup + CI pipeline walkthrough)docs/sonarqube-guide.md(SonarQube setup + Jenkins quality gate walkthrough)docs/ghcr-deployment-guide.md(GHCR image deployment + rollback walkthrough)docs/kafka-events.md(cleanup event contracts and local topic setup)
.env.localcontains host-local values for manually running Spring services..envcontains Docker-network values for both application Compose files and SonarQube..release.envis generated by CI and contains onlyIMAGE_REGISTRYandIMAGE_TAG..env.local.exampleand.env.exampleare the committed, secret-free templates.- Mongo logical DB isolation uses:
MONGO_USER_DATABASEMONGO_PRODUCT_DATABASEMONGO_MEDIA_DATABASEon the same MongoDB instance.
- Docker + Docker Compose
- OpenSSL
- Java 21
- Node.js + npm
mkdir -p backend/certs
openssl req -x509 -nodes -newkey rsa:2048 -sha256 -days 825 \
-keyout backend/certs/gateway.key \
-out backend/certs/gateway.crt \
-subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
openssl pkcs12 -export \
-out backend/certs/gateway.p12 \
-inkey backend/certs/gateway.key \
-in backend/certs/gateway.crt \
-name gateway \
-passout pass:changeitmkdir -p backend/keys
openssl genpkey -algorithm RSA -out backend/keys/private.pem -pkeyopt rsa_keygen_bits:2048
openssl pkey -in backend/keys/private.pem -pubout -out backend/keys/public.pemProvision .env from .env.example and provide .release.env with the published image coordinates.
From repo root:
docker compose --env-file .env --env-file .release.env -f docker-compose.yml pull
docker compose --env-file .env --env-file .release.env -f docker-compose.yml up -d --remove-orphansThis starts backend services plus the Dockerized frontend on https://localhost:4200.
Stop:
docker compose --env-file .env --env-file .release.env -f docker-compose.yml downStop and remove volumes:
docker compose --env-file .env --env-file .release.env -f docker-compose.yml down -vmake dev-upStop the stack with make dev-down, or use make dev-down-v to also remove volumes.
Both Compose files use the pinned community build pgsty/minio:RELEASE.2026-08-04T00-00-00Z.
The previously configured quay.io/minio/minio:latest image is no longer publicly pullable.
This replacement is published by PGSTY, an independent MinIO fork; upstream MinIO now documents source-only distribution.
The existing MinIO service name, ports, credentials, and data volume are retained.
- Start infra (
mongo,minio,discovery-service,kafka) from repo root:
make dev-infra-upTo start only Kafka, run make kafka-up. Inspect it with make kafka-logs
or list topics with make kafka-topics.
Kafka uses the official apache/kafka:4.3.1 image in single-node KRaft mode,
with persistent storage and a health check. Docker services connect to
kafka:9092; services running from the IDE connect to localhost:29092.
Set KAFKA_BOOTSTRAP_SERVERS accordingly in .env and .env.local when
adding the service producers and consumers. Topic auto-creation is disabled.
Product service creates product.events and User service creates
user.events at application startup using Spring Kafka NewTopic beans.
Existing topics are reused. Start Kafka before either application;
startup fails if Kafka is unavailable. make kafka-up starts only the broker.
See Kafka events for the event payloads.
Product deletion publishing and Media cleanup are implemented. Production
Compose also includes a single broker accessible on the backend network.
Product deletion, detached-image cleanup, and avatar replacement/clearing cleanup are implemented.
The kafka-data volume preserves broker state; make dev-down-v removes it
along with the other development volumes.
- Run backend services manually (each in a separate terminal). Start product and user services first so they create the topics consumed by media service:
cd backend/product-service && ./mvnw spring-boot:run
cd backend/user-service && ./mvnw spring-boot:run
cd backend/media-service && ./mvnw spring-boot:run
cd backend/gateway-service && ./mvnw spring-boot:run- Run frontend:
cd frontend
npm install
npm run start:https- Stop infra:
make dev-infra-downIn this mode, manually launched backend services load host-local values from root .env.local.
Frontend in dev mode still runs manually with npm run start:https.
- Frontend:
https://localhost:4200 - Gateway:
https://localhost:8443 - Eureka:
http://localhost:8761 - MinIO API:
http://localhost:9000 - MinIO Console:
http://localhost:9001 - SonarQube:
http://localhost:9002
POST /auth/register- Optional avatar upload:
POST /media/profile - Link avatar to profile:
PATCH /users/mewithavatarMediaId
- Create product:
POST /productswithmediaIds: [] - Upload images:
POST /media/imageswithproductId+files[] - Save image IDs on product:
PUT /products/{id}withmediaIds
User and product discover media service through Eureka with explicit HTTP timeouts; MEDIA_SERVICE_BASE_URL is no longer needed. Avatar uploads preserve previous images until a profile change publishes USER_AVATAR_CHANGED to clean up the previous avatar. Product image metadata is available at GET /media/images/{productId}; the browser loads returned object URLs through Gateway → MinIO.
Product uploads verify ownership through an internal product-service endpoint before validating files. Media discovers product service through Eureka using Spring Cloud LoadBalancer; a fixed PRODUCT_SERVICE_BASE_URL is no longer required. Media accepts JPEG and PNG using Apache Tika content type detection and ImageIO decoding checks; GIF and WebP are unsupported. Deploy product service before media service when introducing this ownership check. See the media-service README for upload failure and cleanup behavior.
docker-compose.dev.ymlbuilds the complete application from local Dockerfiles.docker-compose.ymlruns immutable images selected by.release.env.- Jenkins and SonarQube retain their tool-specific Compose files.
- Frontend Docker runtime uses Nginx with TLS certs from
backend/certsand proxies API routes togateway-service.