A production-grade, scalable e-commerce backend built with Spring Boot and Spring Cloud. Designed with a fully decoupled microservices architecture featuring event-driven communication, graph-based data storage, and gateway-level security.
- Project Overview
- Why These Technology Choices?
- System Architecture
- Services
- Tech Stack
- Security Design
- Communication Strategy
- Event-Driven Flow
- Kafka Topics
- Centralized Configuration
- Getting Started
- API Endpoints
- Authentication Flow
- Project Structure
- API Documentation
Quickart is a fully decoupled microservices-based e-commerce backend. Each service owns its own domain, can be deployed independently, and communicates with other services either synchronously via OpenFeign (when an immediate response is needed) or asynchronously via Apache Kafka (for fire-and-forget events).
Key design highlights:
- JWT authentication is enforced at the API Gateway — no downstream service handles auth logic
- Neo4j graph database is used to model complex entity relationships (users, products, orders)
- Kafka decouples the order → payment → notification pipeline
- Spring Cloud Config Server manages all configuration from a single Git-backed repository
- A shared
commonlibrary provides consistent exception handling and response wrappers across all services
| Technology | Reason |
|---|---|
| Neo4j (Graph DB) | E-commerce data is inherently relational — products have categories, users have wishlists, orders link users to products. Neo4j models these as first-class graph relationships, making traversal queries faster and more natural than SQL joins. |
| Apache Kafka | Order → Payment → Notification is an async pipeline. Using Kafka decouples these services so a slow email server won't delay order processing. It also provides durability — events are not lost if a consumer is temporarily down. |
| API Gateway + JWT | Centralizing authentication at the gateway means downstream services are simpler, faster, and don't need to replicate security logic. The gateway validates the token and passes the user identity via a trusted header (X-User-Email). |
| Spring Cloud Config | Managing configuration across 9 services manually is error-prone. A Git-backed config server means one place to change a value and all services pick it up on restart. |
| OpenFeign | Declarative HTTP client — makes synchronous inter-service calls (e.g., checking product stock before placing an order) feel like local method calls, with built-in load balancing via Eureka. |
| Common Library | Shared exception classes and response wrappers ensure consistent API responses across all services without duplicating code. |
┌───────────────────┐
│ Config Server │ ← Git-backed centralized config
│ Port: 8888 │
└────────┬──────────┘
│ provides config on startup
▼
┌───────────────────┐
│ Eureka Server │ ← Service registry & discovery
│ Port: 8761 │
└────────┬──────────┘
│ all services register here
▼
┌──────────────────────────────────────┐
│ API Gateway │ ← Single entry point
│ Port: 8080 │ JWT validation
│ Spring Cloud Gateway (WebFlux) │ Route + CORS
└───┬──────────┬──────────┬────────────┘
│ │ │
┌──────┘ ┌──────┘ ┌─────┘
▼ ▼ ▼
User Service Product Svc Cart Service ← Business Services
Port: 8087 Port: 8086 Port: 8081 (Neo4j Graph DB)
│
Order Service Payment Service
Port: 8083 Port: 8085
│ │
└──────┬───────┘
│ Apache Kafka (Event Bus)
▼
Notification Service ← Kafka Consumer
Port: 8082 JavaMailSender
Gmail SMTP
| Service | Port | Responsibility |
|---|---|---|
eureka-server |
8761 |
Service registry — all services register and discover each other here |
config-server |
8888 |
Git-backed centralized configuration for all services |
api-gateway |
8080 |
Single entry point — JWT validation, request routing, CORS |
user-service |
8087 |
User registration, login, JWT generation, profile management |
product-service |
8086 |
Product catalog — create, update, search products |
cart-service |
8081 |
Shopping cart — add/remove items, checkout trigger |
order-service |
8083 |
Order creation and lifecycle management |
payment-service |
8085 |
Payment processing, invoice generation |
notification-service |
8082 |
Kafka consumer — sends transactional emails via Gmail SMTP |
common |
library | Shared module — custom exceptions, response wrappers, constants |
| Category | Technology | Version |
|---|---|---|
| Language | Java | 21 |
| Framework | Spring Boot | 4.0.6 |
| Microservices | Spring Cloud | 2025.1.1 |
| Service Discovery | Spring Cloud Netflix Eureka | — |
| Config Management | Spring Cloud Config Server | — |
| API Gateway | Spring Cloud Gateway (WebFlux) | — |
| Inter-service (Sync) | OpenFeign | — |
| Inter-service (Async) | Apache Kafka | — |
| Security | Spring Security + JJWT | 0.12.3 |
| Database | Neo4j Graph Database | — |
| JavaMailSender (Gmail SMTP) | — | |
| API Docs | SpringDoc OpenAPI / Swagger UI | — |
| Build Tool | Maven | 3.8+ |
| Boilerplate | Lombok | — |
Authentication is fully centralized at the API Gateway. No downstream service performs token validation — this keeps them lightweight and focused on business logic.
┌─────────────────────────────────────────────────────────┐
│ JWT Flow │
│ │
│ 1. User logs in → User Service issues accessToken │
│ + refreshToken │
│ │
│ 2. Client sends: Authorization: Bearer <accessToken> │
│ │
│ 3. API Gateway intercepts every request │
│ ├── Validates JWT signature & expiry │
│ ├── VALID → strips token, adds X-User-Email │
│ │ header, forwards to service │
│ └── INVALID → returns 401 Unauthorized │
│ │
│ 4. Downstream services trust X-User-Email header │
│ (set by gateway, never by the client) │
└─────────────────────────────────────────────────────────┘
Token lifecycle:
accessToken— expires in 2 hoursrefreshToken— used to obtain a new access token via/api/user/refresh-token
Public routes (no JWT required): sign-up, verify, sign-in, forgot-password, reset-password, refresh-token
| Type | Technology | When Used | Example |
|---|---|---|---|
| Synchronous | OpenFeign | Immediate response required | Cart → Product (stock check before checkout) |
| Asynchronous | Apache Kafka | Fire-and-forget events | Order → Notification (send confirmation email) |
| Discovery | Eureka | Resolve service host/port | Any Feign call between services |
| Entry Point | API Gateway | All client requests | Auth, routing, CORS handling |
Design decision: Kafka is used for the order → payment → notification pipeline because these steps do not need to be synchronous. A customer placing an order should not wait for the email to send. Feign is used only where the calling service genuinely cannot proceed without a response (e.g., stock validation).
User places order
│
▼
Cart Service ──── Feign ────► Product Service
(validate stock availability)
│
│ Kafka: cart-checkout
▼
Order Service (saves order, confirms)
│
├──── Kafka: order-confirmed ────────────────────────┐
│ │
│ Notification Service
│ (order confirmation email)
│
└──── Kafka: order-confirmed ────► Payment Service
│
│ Kafka: payment-completed
▼
Notification Service
(payment receipt email)
│
│ Kafka: invoice-created
▼
Notification Service
(invoice email)
| Topic | Producer | Consumer(s) | Purpose |
|---|---|---|---|
cart-checkout |
cart-service | order-service | Trigger order creation after cart checkout |
order-confirmed |
order-service | payment-service, notification-service | Notify payment + send order confirmation email |
payment-completed |
payment-service | notification-service | Send payment receipt email |
invoice-created |
payment-service | notification-service | Send invoice email to customer |
stock-updated |
product-service | cart-service | Sync inventory changes to cart |
All service configurations are stored in a dedicated Git repository, served by the Config Server at startup. No sensitive values are hardcoded in any service.
👉 Config repo: quickart-configs
quickart-configs/
├── application.properties ← Shared by ALL services (Eureka URL, etc.)
├── api-gateway.yml
├── user-service.properties
├── product-service.properties
├── cart-service.properties
├── order-service.properties
├── payment-service.properties
└── notification-service.properties
Each service only needs two lines in its local bootstrap.properties:
spring.application.name=user-service
spring.config.import=configserver:http://localhost:8888The Config Server then delivers the correct config file based on the service name.
- Java 21
- Maven 3.8+
- Neo4j (local install or AuraDB cloud)
- Apache Kafka + Zookeeper running locally
- Gmail account with an App Password enabled
git clone https://github.com/SKgain/quickart-backend.git
cd quickart-backendThe common module is a shared library used by all services. Install it to your local Maven repository first:
cd common
mvn clean install
cd ..Update the following in the quickart-configs repo or set as environment variables:
# Neo4j
spring.neo4j.uri=bolt://localhost:7687
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=your_password
# Apache Kafka
spring.kafka.bootstrap-servers=localhost:9092
# Gmail SMTP (Notification Service)
spring.mail.username=your@gmail.com
spring.mail.password=your_app_password
# JWT Secret (User Service & API Gateway)
jwt.secret=your_base64_encoded_secret_key
jwt.access-token-expiry=7200000
⚠️ Order matters. Services depend on each other at startup.
# Step 1 — Eureka Server (must be first)
cd eureka-server
mvn spring-boot:run
# Step 2 — Config Server (must be second)
cd ../config-server
mvn spring-boot:run
# Step 3 — API Gateway
cd ../api-gateway
mvn spring-boot:run
# Step 4 — Business Services (any order)
cd ../user-service && mvn spring-boot:run
cd ../product-service && mvn spring-boot:run
cd ../cart-service && mvn spring-boot:run
cd ../order-service && mvn spring-boot:run
cd ../payment-service && mvn spring-boot:run
cd ../notification-service && mvn spring-boot:run| Dashboard | URL |
|---|---|
| Eureka Dashboard | http://localhost:8761 |
| API Gateway | http://localhost:8080 |
| Swagger UI (all services) | http://localhost:8080/swagger-ui.html |
All requests go through the API Gateway at port
8080. Do not call services directly.
| Method | Endpoint | Description | Auth |
|---|---|---|---|
POST |
/api/user/sign-up |
Register a new user | Public |
GET |
/api/user/verify |
Verify email with token | Public |
POST |
/api/user/sign-in |
Login, receive access + refresh tokens | Public |
POST |
/api/user/resend-verification |
Resend email verification | Public |
POST |
/api/user/forgot-password |
Send password reset email | Public |
POST |
/api/user/reset-password |
Reset password using token | Public |
POST |
/api/user/refresh-token |
Exchange refresh token for new access token | Public |
POST |
/api/user/logout |
Invalidate user session | 🔒 Private |
GET |
/api/user/profile |
Get authenticated user's profile | 🔒 Private |
Step 1 → POST /api/user/sign-up
↓
Verification email sent to user's inbox
Step 2 → GET /api/user/verify?token=xxx&email=xxx
↓
Account activated
Step 3 → POST /api/user/sign-in
↓
Response: { accessToken, refreshToken }
Step 4 → All subsequent requests:
Header: Authorization: Bearer <accessToken>
↓
API Gateway validates → adds X-User-Email header → forwards request
Step 5 → Access token expires (2 hours):
POST /api/user/refresh-token
↓
Response: { new accessToken, new refreshToken }
quickart-backend/
│
├── common/ ← Shared library (install first)
│ └── src/main/java/com/skgain/common/
│ ├── exceptions/ # Custom exceptions + GlobalExceptionHandler
│ ├── responses/ # ApiResponse wrapper, EmailResponse
│ └── constants/ # ErrorCode enum, SecurityConstant
│
├── eureka-server/ ← Service registry
│ └── src/main/resources/
│ └── application.properties
│
├── config-server/ ← Centralized config server
│ └── src/main/resources/
│ └── application.properties # Points to quickart-configs Git repo
│
├── api-gateway/ ← Gateway — JWT filter, routing, CORS
│ └── src/main/java/com/skgain/api_gateway/
│ ├── auth/ # JwtAuthFilter, JwtService, SecurityConfig
│ └── configs/ # Route definitions, GatewayConfig
│
├── user-service/ ← Auth + user domain (Neo4j)
│ └── src/main/java/com/skgain/user_service/
│ ├── auth/ # JWT generation, SecurityConfig
│ ├── node/ # Neo4j node entities (User, Token, etc.)
│ ├── repositories/ # Neo4j repositories
│ ├── services/ # Business logic
│ ├── feigns/ # Feign clients to other services
│ └── controllers/ # REST controllers
│
├── product-service/ ← Product catalog domain (Neo4j)
├── cart-service/ ← Cart domain (Neo4j)
├── order-service/ ← Order domain (Neo4j)
├── payment-service/ ← Payment domain (Neo4j)
│
└── notification-service/ ← Email domain (Kafka consumer)
└── src/main/resources/
└── templates/ # HTML email templates
├── signup-verification-email.html
└── reset-password-email.html
All services are aggregated under the API Gateway's Swagger UI. No need to access individual service Swagger pages.
| URL | Description |
|---|---|
http://localhost:8080/swagger-ui.html |
Unified Swagger UI for all services |
Use the service dropdown in Swagger UI to switch between:
- User Service
- Product Service
- Cart Service
- Order Service
- Payment Service
- Notification Service
| Kafka Trigger | Description | |
|---|---|---|
| Signup verification | — (direct) | Sent on registration, contains verification link |
| Password reset | — (direct) | Contains secure reset link |
| Order confirmation | order-confirmed |
Sent to customer after order is placed |
| Payment receipt | payment-completed |
Sent after successful payment |
| Invoice | invoice-created |
Invoice PDF/details sent to customer |
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature-name - Commit with a meaningful message:
git commit -m "feat: describe your change" - Push and open a Pull Request
This project is licensed under the MIT License.