A production-ready, event-driven microservices architecture for e-commerce applications built with Spring Boot, Kafka, and PostgreSQL.
👉 Read the LOCAL SETUP GUIDE (LOCAL_SETUP.md) for full instructions.
For the impatient:
./start.sh- System Overview
- Architecture
- Services Deep Dive
- Domain Models
- Event Flow
- API Reference
- Getting Started
- Configuration
- Deployment
- Monitoring
This platform provides a complete e-commerce backend with:
- User Management: Registration, authentication, profile management
- Order Processing: Order creation, tracking, and lifecycle management
- Payment Processing: Secure payment handling with idempotency
- Notification System: Email notifications for all major events
- Event-Driven Architecture: Asynchronous communication via Kafka
- Outbox Pattern: Reliable event publishing with exactly-once semantics
| Component | Technology |
|---|---|
| API Gateway | Spring Cloud Gateway |
| Microservices | Spring Boot 3.2.5 |
| Message Broker | Apache Kafka |
| Databases | PostgreSQL 16 |
| Caching | Redis (optional) |
| Security | JWT (JJWT 0.12.5) |
| Monitoring | Spring Boot Actuator, Micrometer |
| Resilience | Resilience4j (Circuit Breaker, Retry, Bulkhead) |
┌─────────────────────────────────────────────────────────────────────┐
│ Client Application │
│ (Web App / Mobile App / API Consumer) │
└──────────────────────────┬─────────────────────────────────────────┘
│ HTTPS / REST
▼
┌─────────────────────────────────────────────────────────────────────┐
│ API Gateway (Port 8080) │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ • JWT Authentication & Validation │ │
│ │ • Request Routing to Services │ │
│ │ • Rate Limiting (60 req/min default) │ │
│ │ • CORS Configuration │ │
│ │ • Security Headers (CSP, HSTS, XSS Protection) │ │
│ │ • Correlation ID Tracking │ │
│ └─────────────────────────────────────────────────────────────┘ │
└──────────────────────────┬─────────────────────────────────────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ User Service │ │ Order Service │ │Payment Service│
│ Port 8081 │ │ Port 8082 │ │ Port 8083 │
│ │ │ │ │ │
│ • Register │ │ • Create │ │ • Process │
│ • Login │ │ • Track │ │ • Verify │
│ • Profile │ │ • Manage │ │ • Idempotency │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ User DB │ │ Order DB │ │ Payment DB │
│ Port 5433 │ │ Port 5434 │ │ Port 5435 │
│ PostgreSQL │ │ PostgreSQL │ │ PostgreSQL │
└───────────────┘ └───────────────┘ └───────────────┘
│ │ │
│ │ │
└──────────────────┼──────────────────┘
│ Events via Kafka
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Kafka Cluster │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Topics: │ │
│ │ • user-events (Partitions: 3, Replicas: 1) │ │
│ │ • order-events (Partitions: 3, Replicas: 1) │ │
│ │ • payment-events (Partitions: 3, Replicas: 1) │ │
│ │ • order-events.dlq (Dead Letter Queue) │ │
│ └─────────────────────────────────────────────────────────────┘ │
└──────────────────────────┬─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Notification Service (Port 8084) │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Consumes events and sends: │ │
│ │ • Welcome emails (UserCreated) │ │
│ │ • Order confirmations (OrderCreated) │ │
│ │ • Payment receipts (PaymentProcessed) │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
- Synchronous: REST API calls via API Gateway (for client requests)
- Asynchronous: Kafka events (for service-to-service communication)
- Outbox Pattern: Database + Poller for reliable event publishing
Purpose: Single entry point for all client requests
Responsibilities:
- JWT token validation on protected routes
- Route requests to appropriate services
- Add user context headers (X-User-Id, X-User-Role, X-User-Email)
- Rate limiting (configurable per endpoint)
- CORS handling
- Security headers
Routes:
/api/users/register → User Service (Public)
/api/users/login → User Service (Public)
/api/users/** → User Service (Protected)
/api/orders/** → Order Service (Protected)
/api/payments/** → Payment Service (Protected)
Configuration:
security:
rate-limit:
requests-per-minute: 60
max-request-size: 1048576 # 1MBPurpose: User registration, authentication, and profile management
Database: PostgreSQL (user_db, Port 5433)
Entities:
@Entity
@Table(name = "users")
public class User {
UUID id; // Primary key
String email; // Unique, indexed
String password; // BCrypt hashed
String fullName;
Role role; // USER or ADMIN
LocalDateTime createdAt;
LocalDateTime updatedAt;
}API Endpoints:
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/users/register | Public | Register new user |
| POST | /api/users/login | Public | Authenticate user |
| GET | /api/users/profile | Protected | Get current user profile |
Events Published:
UserCreated→ user-events topic
Outbox Pattern:
- Events saved to
outbox_eventstable - Poller runs every 100ms to publish to Kafka
- Guarantees exactly-once event publishing
Purpose: Order creation, tracking, and lifecycle management
Database: PostgreSQL (order_db, Port 5434)
Resilience Patterns:
- Circuit Breaker for database operations
- Retry with exponential backoff
- Bulkhead for concurrency limiting
- Timeout configuration
Entities:
@Entity
@Table(name = "orders")
public class Order {
UUID id; // Primary key
UUID userId; // Who placed the order
String userEmail; // For notifications
BigDecimal totalAmount; // Calculated from items
OrderStatus status; // CREATED, PAID, SHIPPED, DELIVERED, CANCELLED
List<OrderItem> items; // One-to-many relationship
LocalDateTime createdAt;
LocalDateTime updatedAt;
}@Entity
@Table(name = "order_items")
public class OrderItem {
UUID id;
Order order; // Many-to-one
UUID productId;
Integer quantity;
BigDecimal price; // Price at time of order
}Order Status Flow:
CREATED → PAID → SHIPPED → DELIVERED
↓
CANCELLED
API Endpoints:
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/orders | Protected | Create new order |
| GET | /api/orders/{id} | Protected | Get order details |
| GET | /api/orders | Protected | List user's orders (paginated) |
Events:
- Published:
OrderCreated→ order-events topic - Consumed:
PaymentCompletedEventfrom payment-events topic
Caching:
- Order details cached for 10 minutes
- Order lists cached for 1 minute
- Redis support (optional, configurable)
Purpose: Payment processing with idempotency guarantees
Database: PostgreSQL (payment_db, Port 5435)
Entities:
@Entity
@Table(name = "payments")
public class Payment {
UUID id;
UUID orderId; // Links to order
BigDecimal amount;
PaymentStatus status; // PENDING, COMPLETED, FAILED
String transactionId; // External payment gateway ID
String userEmail;
LocalDateTime createdAt;
LocalDateTime updatedAt;
}@Entity
@Table(name = "idempotent_requests")
public class IdempotentRequest {
UUID id;
String idempotencyKey; // Unique client-provided key
String requestHash; // Hash of request content
String requestType; // e.g., "PROCESS_PAYMENT"
String responsePayload; // Cached response
Integer responseStatusCode;
Instant expiresAt; // Auto-cleanup after 24 hours
}API Endpoints:
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/payments/process | Protected | Process payment for order |
| GET | /api/payments/order/{orderId} | Protected | Get payment status |
Events:
- Published:
PaymentProcessed→ payment-events topic - Consumed:
OrderCreatedfrom order-events topic
Idempotency:
- Client provides
X-Idempotency-Keyheader - Same key + request = same response (24h window)
- Prevents duplicate charges
Purpose: Send email notifications for business events
Database: None (stateless)
Events Consumed:
| Event | Topic | Action |
|---|---|---|
| UserCreated | user-events | Send welcome email |
| OrderCreated | order-events | Send order confirmation |
| PaymentProcessed | payment-events | Send payment receipt |
Configuration:
spring:
kafka:
consumer:
group-id: notification-service-group
auto-offset-reset: earliestProducts are referenced by ID across services (product data managed separately):
{
"productId": "550e8400-e29b-41d4-a716-446655440000",
"name": "Wireless Headphones",
"price": 99.99,
"currency": "USD",
"sku": "WH-001"
}{
"items": [
{
"productId": "550e8400-e29b-41d4-a716-446655440000",
"quantity": 2,
"price": 99.99
},
{
"productId": "550e8400-e29b-41d4-a716-446655440001",
"quantity": 1,
"price": 49.99
}
]
}{
"id": "order-uuid",
"userId": "user-uuid",
"userEmail": "customer@example.com",
"totalAmount": 249.97,
"status": "CREATED",
"items": [
{
"id": "item-uuid",
"productId": "550e8400-e29b-41d4-a716-446655440000",
"quantity": 2,
"price": 99.99,
"subtotal": 199.98
}
],
"createdAt": "2024-01-15T10:30:00",
"updatedAt": "2024-01-15T10:30:00"
}Client → API Gateway → User Service
│
│ (Transaction)
▼
┌──────────────┐
│ User DB │
└──────────────┘
│
│ (Outbox Pattern)
▼
┌──────────────┐
│ Outbox Table │
└──────────────┘
│
│ (Poller)
▼
┌──────────────┐
│ Kafka Topic │
│ user-events │
└──────────────┘
│
▼
┌──────────────┐
│ Notification │
│ Service │
└──────────────┘
│
▼
Send Welcome Email
Client → API Gateway → Order Service
│
│ (Transaction: Order + Outbox)
▼
┌──────────────┐ ┌──────────────┐
│ Order DB │ │ Outbox Table │
└──────────────┘ └──────────────┘
│
│ (Poller)
▼
┌──────────────┐
│ Kafka Topic │
│ order-events │
└──────────────┘
│
┌─────────────┴─────────────┐
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│Payment Service│ │ Notification │
└──────────────┘ │ Service │
│ └──────────────┘
│ │
▼ ▼
Process Payment Send Order
(Auto-triggered) Confirmation
Payment Service (consumed OrderCreated)
│
│ (Transaction)
▼
┌──────────────┐ ┌──────────────┐
│ Payment DB │ │ Outbox Table │
└──────────────┘ └──────────────┘
│
│ (Poller)
▼
┌──────────────┐
│ Kafka Topic │
│payment-events│
└──────────────┘
│
▼
┌──────────────┐
│Order Service │ (updates order status)
└──────────────┘
│
▼
┌──────────────┐
│ Notification │ (sends receipt)
└──────────────┘
All protected endpoints require JWT token in Authorization header:
Authorization: Bearer <jwt_token>
POST /api/users/register
Content-Type: application/json
{
"email": "user@example.com",
"password": "securePassword123",
"fullName": "John Doe"
}Response:
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"type": "Bearer",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"fullName": "John Doe",
"role": "USER",
"createdAt": "2024-01-15T10:30:00"
}
}POST /api/users/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "securePassword123"
}Response: Same as Register
GET /api/users/profile
Authorization: Bearer <token>Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"fullName": "John Doe",
"role": "USER",
"createdAt": "2024-01-15T10:30:00"
}POST /api/orders
Authorization: Bearer <token>
Content-Type: application/json
X-Idempotency-Key: <uuid> # Optional
{
"items": [
{
"productId": "550e8400-e29b-41d4-a716-446655440000",
"quantity": 2,
"price": 29.99
}
]
}Response:
{
"id": "order-uuid",
"userId": "user-uuid",
"userEmail": "user@example.com",
"totalAmount": 59.98,
"status": "CREATED",
"items": [...],
"createdAt": "2024-01-15T10:30:00",
"updatedAt": "2024-01-15T10:30:00"
}GET /api/orders/{orderId}
Authorization: Bearer <token>GET /api/orders?page=0&size=10
Authorization: Bearer <token>GET /api/payments/order/{orderId}
Authorization: Bearer <token>Response:
{
"id": "payment-uuid",
"orderId": "order-uuid",
"amount": 59.98,
"status": "COMPLETED",
"transactionId": "txn_123456",
"createdAt": "2024-01-15T10:31:00"
}- Docker 20.10+ with 8GB+ RAM
- Docker Compose 2.0+
- Maven 3.8+ (optional)
- Java 17+ (optional, for local development)
# 1. Clone and navigate
cd e-commerce-microservices
# 2. Set environment variables
cp .env.example .env
# Edit .env and set secure JWT_SECRET
# 3. Build and start all services
docker-compose up -d --build
# 4. Wait for health checks (30-60 seconds)
docker-compose ps
# 5. Test the API
curl http://localhost:8080/actuator/health# 1. Build the project
mvn clean install -DskipTests
# 2. Start infrastructure (if using Docker for infra only)
docker-compose up -d zookeeper kafka user-db order-db payment-db
# 3. Set environment
export JWT_SECRET="your-32-char-secret-here"
export DB_PASSWORD="postgres"
# 4. Run services individually
cd user-service && mvn spring-boot:run
cd order-service && mvn spring-boot:run
# ... etc| Variable | Default | Description |
|---|---|---|
JWT_SECRET |
(required) | 32+ character secret for JWT signing |
JWT_EXPIRATION |
86400000 | Token expiration in ms (24h default) |
DB_URL |
jdbc:postgresql://localhost:5432/... | Database connection URL |
DB_USERNAME |
postgres | Database username |
DB_PASSWORD |
postgres | Database password |
KAFKA_BOOTSTRAP_SERVERS |
localhost:9092 | Kafka broker addresses |
CACHE_TYPE |
simple | Cache provider (simple/redis) |
REDIS_HOST |
localhost | Redis hostname |
REDIS_PORT |
6379 | Redis port |
| Service | Port | Database Port |
|---|---|---|
| API Gateway | 8080 | - |
| User Service | 8081 | 5433 |
| Order Service | 8082 | 5434 |
| Payment Service | 8083 | 5435 |
| Notification Service | 8084 | - |
| Kafka | 9092 | - |
| Zookeeper | 2181 | - |
# Start everything
docker-compose up -d
# View logs
docker-compose logs -f [service-name]
# Scale order service
docker-compose up -d --scale order-service=3
# Stop everything
docker-compose down -v-
Security:
- Use strong JWT_SECRET (32+ chars)
- Enable HTTPS/TLS
- Set secure database passwords
- Use secret management (Vault, AWS Secrets Manager)
-
Database:
- Use
ddl-auto: validate(never create-drop) - Run Flyway migrations manually
- Enable connection pooling (HikariCP)
- Set up read replicas
- Use
-
Kafka:
- Use Kafka cluster (not single node)
- Configure replication factor >= 3
- Set up monitoring (Kafka Manager)
- Enable SSL/SASL for security
-
Caching:
- Enable Redis in production
- Configure appropriate TTLs
- Monitor cache hit rates
-
Monitoring:
- Prometheus for metrics
- Grafana for dashboards
- ELK stack for logging
- PagerDuty for alerts
All services expose actuator endpoints:
# Health check
curl http://localhost:8080/actuator/health
curl http://localhost:8081/actuator/health
# Metrics
curl http://localhost:8082/actuator/metrics
# Circuit breaker status
curl http://localhost:8082/actuator/circuitbreakers
# Prometheus metrics
curl http://localhost:8082/actuator/prometheus# List topics
docker-compose exec kafka kafka-topics --list --bootstrap-server localhost:29092
# Topic details
docker-compose exec kafka kafka-topics --describe --topic order-events --bootstrap-server localhost:29092
# Consume messages
docker-compose exec kafka kafka-console-consumer \
--bootstrap-server localhost:29092 \
--topic order-events \
--from-beginning
# Check consumer groups
docker-compose exec kafka kafka-consumer-groups \
--bootstrap-server localhost:29092 \
--list# Connect to database
docker-compose exec user-db psql -U postgres -d user_db
# Check tables\dt
# View outbox events
SELECT * FROM outbox_events WHERE processed = false;# Run all tests
mvn test
# Run with Testcontainers
mvn verify
# Run specific service tests
cd order-service && mvn test# 1. Register user
curl -X POST http://localhost:8080/api/users/register \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"password123","fullName":"Test User"}'
# 2. Login (save token)
TOKEN=$(curl -X POST http://localhost:8080/api/users/login \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"password123"}' | jq -r '.token')
# 3. Create order
curl -X POST http://localhost:8080/api/orders \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"items":[{"productId":"prod-001","quantity":2,"price":29.99}]}'
# 4. Check notifications
docker-compose logs notification-service1. Services fail to start
- Check database connection strings
- Verify JWT_SECRET is set and >= 32 chars
- Check Kafka bootstrap servers
2. Kafka connection errors
- Ensure Zookeeper is running before Kafka
- Check
KAFKA_ADVERTISED_LISTENERSconfiguration - Verify port 9092 is not in use
3. Database connection refused
- Check if database containers are healthy
- Verify database ports (5433, 5434, 5435)
- Check firewall settings
4. Events not being published
- Check outbox_events table for unprocessed events
- Verify KafkaTemplate bean is created
- Check Kafka topic exists
# Enable debug logging
export LOG_LEVEL=DEBUG
# Run with debug
mvn spring-boot:run -Dspring-boot.run.arguments=--debug- Database-per-Service: Each service owns its data
- API Gateway: Single entry point with cross-cutting concerns
- Outbox Pattern: Reliable event publishing
- Event Sourcing: Order service tracks domain events
- Saga Pattern: Distributed transaction coordination
- Circuit Breaker: Fault tolerance (Resilience4j)
- CQRS: Separate read/write models (optional)
- Fork the repository
- Create feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open Pull Request
MIT License - See LICENSE file for details
For issues and questions:
- Create GitHub Issue
- Check existing documentation
- Review FIXES_APPLIED.md for recent changes