Enrique Valdivia Rios Portafolio - Senior Backend Engineer | Java · Spring Boot · Kubernetes | Fintech & Payments | Scalable Distributed Systems

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

ComponenteTecnología
FrameworkSpring Boot 3.2 + Spring Cloud
API GatewaySpring Cloud Gateway + Resilience4j
MensajeríaApache Kafka
DB relacionalPostgreSQL 16 (orders, payments)
DB documentalMongoDB 7 (products, notifications)
ObservabilidadPrometheus + Grafana + Micrometer
ContenedoresDocker + Docker Compose
OrquestaciónKubernetes 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:


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

Ver código en GitHub