E-Commerce Microservices Platform
An e-commerce platform built with real microservices architecture: independent services, asynchronous communication via Kafka, polyglot persistence, and Kubernetes deployment.
Full source code available on my GitHub repository.
Architecture
Client → API Gateway (8080)
├── /api/orders/** → Order Service (PostgreSQL + Kafka producer)
├── /api/products/** → Product Service (MongoDB)
├── /api/payments/** → Payment Service (PostgreSQL + Kafka consumer/producer)
└── /api/notifications → Notification Service (MongoDB + Kafka consumer)
The event flow is fully asynchronous:
Order Service →[order.created]→ Payment Service →[payment.processed]→ Notification Service
↑
Order Service →[order.created]→──────────────────────────────────────────┘
Tech Stack
| Component | Technology |
|---|---|
| Framework | Spring Boot 3.2 + Spring Cloud |
| API Gateway | Spring Cloud Gateway + Resilience4j |
| Messaging | Apache Kafka |
| Relational DB | PostgreSQL 16 (orders, payments) |
| Document DB | MongoDB 7 (products, notifications) |
| Observability | Prometheus + Grafana + Micrometer |
| Containers | Docker + Docker Compose |
| Orchestration | Kubernetes 1.28 |
Implemented Services
Order Service — Port 8081
Manages the order lifecycle. On creation, publishes an order.created event to Kafka:
@Transactional
public OrderResponse createOrder(CreateOrderRequest request) {
Order saved = orderRepository.save(order);
kafkaTemplate.send("order.created",
saved.getId().toString(),
new OrderCreatedEvent(saved.getId(), saved.getCustomerId(),
saved.getProductId(), saved.getQuantity(), saved.getAmount()));
return OrderResponse.from(saved);
}
Endpoints: POST /api/orders · GET /api/orders/{id} · GET /api/orders?customerId= · POST /api/orders/{id}/cancel
Product Service — Port 8082
Full product catalog CRUD backed by MongoDB:
Endpoints: POST /api/products · GET /api/products · GET /api/products/{id} · GET /api/products?category= · PUT /api/products/{id} · DELETE /api/products/{id}
Payment Service — Port 8083
Consumes order.created, processes the payment, and publishes payment.processed:
@KafkaListener(topics = "order.created", groupId = "payment-group")
public void onOrderCreated(OrderCreatedEvent event) {
paymentService.processPayment(event);
}
The service simulates approval/rejection by amount (>10,000 is declined):
if (event.amount().intValue() > 10000) {
payment.setStatus(PaymentStatus.FAILED);
payment.setFailureReason("Amount exceeds limit");
} else {
payment.setStatus(PaymentStatus.COMPLETED);
}
kafkaTemplate.send("payment.processed", ...);
Endpoints: GET /api/payments/{id} · GET /api/payments/order/{orderId}
Notification Service — Port 8084
Consumes both events and persists notifications in MongoDB:
@KafkaListener(topics = "order.created", groupId = "notification-group")
public void onOrderCreated(OrderCreatedEvent event) { ... }
@KafkaListener(topics = "payment.processed", groupId = "notification-group")
public void onPaymentProcessed(PaymentProcessedEvent event) { ... }
API Gateway — Port 8080
Request routing with Resilience4j circuit breaker on all routes:
- Sliding window: 10 requests
- Failure rate threshold: 50%
- Open state wait: 10 seconds
Testing
Order Service: 7 unit tests (service) + 6 controller tests with MockMvc
Product Service: 8 unit tests (service)
Payment Service: 6 unit tests — covering completed payment, failed payment, lookup by ID and by orderId
Notification Service: 4 tests — verifying notification content for each event type
@Test
void handlePaymentProcessed_failed_savesFailureNotification() {
PaymentProcessedEvent event = new PaymentProcessedEvent(
UUID.randomUUID(), orderId, "customer-1", "FAILED",
new BigDecimal("20000.00"), "Amount exceeds limit");
notificationService.handlePaymentProcessed(event);
ArgumentCaptor<Notification> captor = ArgumentCaptor.forClass(Notification.class);
verify(notificationRepository).save(captor.capture());
assertThat(captor.getValue().getMessage()).contains("Amount exceeds limit");
}
How to Run
git clone https://github.com/enriquevaldivia1988/microservices-k8s.git
cd microservices-k8s
# Start all infrastructure and services
docker-compose up -d
# Verify services are healthy
curl http://localhost:8080/actuator/health # API Gateway
curl http://localhost:8081/actuator/health # Order Service
# Create an order
curl -X POST http://localhost:8080/api/orders \
-H "Content-Type: application/json" \
-d '{"customerId":"customer-1","productId":"prod-1","quantity":2,"amount":99.99}'
# Deploy to Kubernetes
kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/kafka-deployment.yaml
kubectl apply -f k8s/
Prometheus: http://localhost:9090 | Grafana: http://localhost:3000 (admin/admin)
Conclusion
This project demonstrates a production-ready microservices architecture: decoupled services, real asynchronous communication with Kafka, polyglot persistence (PostgreSQL + MongoDB), observability with Prometheus/Grafana, and Kubernetes deployment with health probes and resource limits.
Enrique Valdivia