E-Commerce Microservices Platform
Una plataforma de e-commerce construida con arquitectura de microservicios real: servicios independientes, comunicación asíncrona via Kafka, múltiples bases de datos y despliegue en Kubernetes.
El código completo está en mi repositorio de GitHub.
Arquitectura
Cliente → 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)
El flujo de eventos es completamente asíncrono:
Order Service →[order.created]→ Payment Service →[payment.processed]→ Notification Service
↑
Order Service →[order.created]→──────────────────────────────────────────┘
Stack Tecnológico
| Componente | Tecnología |
|---|---|
| Framework | Spring Boot 3.2 + Spring Cloud |
| API Gateway | Spring Cloud Gateway + Resilience4j |
| Mensajería | Apache Kafka |
| DB relacional | PostgreSQL 16 (orders, payments) |
| DB documental | MongoDB 7 (products, notifications) |
| Observabilidad | Prometheus + Grafana + Micrometer |
| Contenedores | Docker + Docker Compose |
| Orquestación | Kubernetes 1.28 |
Servicios Implementados
Order Service — Puerto 8081
Gestiona el ciclo de vida de órdenes. Al crear una orden, publica un evento order.created en 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 — Puerto 8082
CRUD completo de catálogo de productos sobre 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 — Puerto 8083
Consume el evento order.created, procesa el pago y publica payment.processed:
@KafkaListener(topics = "order.created", groupId = "payment-group")
public void onOrderCreated(OrderCreatedEvent event) {
paymentService.processPayment(event);
}
El servicio simula aprobación/rechazo según el monto (>10.000 es rechazado):
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 — Puerto 8084
Consume ambos eventos y persiste notificaciones en 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 — Puerto 8080
Enrutamiento con circuit breaker Resilience4j en todas las rutas:
- Ventana deslizante: 10 requests
- Umbral de fallo: 50%
- Estado abierto: 10 segundos
Testing
Order Service: 7 tests unitarios (servicio) + 6 tests de controlador con MockMvc
Product Service: 8 tests unitarios (servicio)
Payment Service: 6 tests unitarios — cubren pago completado, pago fallido, consulta por ID y por orderId
Notification Service: 4 tests — verifican el contenido de las notificaciones para cada tipo de evento
@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");
}
Cómo Ejecutar
git clone https://github.com/enriquevaldivia1988/microservices-k8s.git
cd microservices-k8s
# Levantar toda la infraestructura y servicios
docker-compose up -d
# Verificar que los servicios están sanos
curl http://localhost:8080/actuator/health # API Gateway
curl http://localhost:8081/actuator/health # Order Service
# Crear una orden
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}'
# Desplegar en 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)
Conclusión
Este proyecto demuestra una arquitectura de microservicios lista para producción: servicios desacoplados, comunicación asíncrona real con Kafka, persistencia políglota (PostgreSQL + MongoDB), observabilidad con Prometheus/Grafana y despliegue en Kubernetes con health probes y resource limits.
Enrique Valdivia