Para consolidar los conceptos del tutorial diseñaremos una plataforma de comercio electrónico llamada TiendaNova. El objetivo es mostrar cómo dividir el dominio en servicios, definir contratos claros, preparar el despliegue y habilitar la observabilidad básica. A lo largo del desarrollo incluiremos fragmentos de código en Java, descripciones de APIs y configuraciones de infraestructura que sirven como punto de partida para proyectos reales.
El ecosistema de TiendaNova se organiza en tres dominios principales: catálogo, pedidos y pagos. Cada microservicio es responsable de una parte del viaje del cliente, cuenta con su propia base de datos y expone APIs independientes. La comunicación directa se realiza mediante REST y los eventos se emplean para sincronizar datos no críticos en tiempo real, como la generación de recomendaciones.
La plataforma se complementa con un gateway que centraliza la seguridad, un sistema de identidad externo y un conjunto de herramientas de monitoreo y logging. El siguiente diagrama conceptual resume la interacción entre servicios:
[Cliente] --> [API Gateway] --> [Catalog Service]
|--> [Order Service] --> [Payment Service]
'--> [Notification Service]
[Order Service] -- evento --> [Recommendation Service]
[Payment Service] -- logs --> [Central Log]
[Todos] -- métricas --> [Prometheus]
Esta arquitectura enfatiza la independencia de despliegue y la extensibilidad. Si se necesita agregar un Fulfillment Service para coordinar envíos, basta con publicar nuevos eventos desde el servicio de pedidos y suscribirse a ellos.
Cada microservicio se implementa con un conjunto de controladores, lógica de dominio y repositorios. Los modelos de datos se diseñan según la necesidad del dominio, evitando compartir esquemas. El catálogo utiliza una base documental para almacenar productos de forma flexible, mientras que pedidos y pagos se apalancan en bases relacionales para garantizar integridad transaccional.
El servicio de catálogo expone endpoints para listar productos, obtener detalles y administrar el inventario. La siguiente interfaz resume la definición de la API.
@RestController
@RequestMapping("/api/catalog")
class CatalogController {
private final CatalogService service;
CatalogController(CatalogService service) {
this.service = service;
}
@GetMapping
public Page<ProductDto> list(@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "12") int size) {
return service.findAll(page, size);
}
@GetMapping("/{sku}")
public ProductDto detail(@PathVariable String sku) {
return service.findBySku(sku);
}
@PostMapping
public ResponseEntity<ProductDto> create(@RequestBody CreateProductCommand command) {
ProductDto created = service.create(command);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
}
El contrato REST se documenta con un esquema OpenAPI para facilitar la generación de clientes y pruebas automáticas. El servicio persiste productos en una colección products dentro de una base NoSQL, incluyendo precios, categorías y atributos dinámicos.
Los pedidos se registran en una base relacional con tablas orders y order_items. El servicio de pagos mantiene su propio esquema con la tabla payments y estados de autorización. Una vez confirmado un pago, el servicio de pedidos actualiza su estado y publica un evento que habilita la preparación del pedido.
@Entity
@Table(name = "orders")
public class OrderEntity {
@Id
private String id;
private String customerId;
private BigDecimal total;
private Instant createdAt;
@Enumerated(EnumType.STRING)
private OrderStatus status;
@OneToMany(mappedBy = "orderId", cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrderItemEntity> items = new ArrayList<>();
}
Al mantener bases independientes se reduce la necesidad de transacciones distribuidas. Las integraciones se realizan a través de eventos y APIs idempotentes.
Para acelerar el desarrollo local se utiliza Docker Compose, que levanta los servicios y sus dependencias con un comando. En entornos de mayor escala se adopta Kubernetes para gestionar la disponibilidad, escalado y actualizaciones.
El siguiente archivo define un entorno que incluye los microservicios, una base PostgreSQL y un broker de eventos.
version: "3.9"
services:
catalog-service:
build: ./catalog-service
ports:
- "8081:8080"
environment:
- SPRING_PROFILES_ACTIVE=docker
order-service:
build: ./order-service
ports:
- "8082:8080"
depends_on:
- postgres
- kafka
payment-service:
build: ./payment-service
ports:
- "8083:8080"
depends_on:
- postgres
- kafka
postgres:
image: postgres:15
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
ports:
- "5432:5432"
kafka:
image: bitnami/kafka:3
environment:
- KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181
ports:
- "9092:9092"
zookeeper:
image: bitnami/zookeeper:3.8
ports:
- "2181:2181"
Con este archivo se lanza el ecosistema ejecutando docker compose up. Las variables de entorno se adaptan para conectar cada servicio a la infraestructura compartida.
Para el entorno de producción se crean manifiestos que definen Deployments, Services y ConfigMaps. A continuación se muestra un extracto del despliegue del servicio de pedidos con Horizontal Pod Autoscaler.
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: registry/tiendanova/order-service:1.2.0
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: order-service-config
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
El despliegue se integra con pipelines de CI/CD que actualizan las imágenes y supervisan el estado del rollout, permitiendo rollback automático si la nueva versión se degrada.
La operación confiable del sistema requiere recopilar métricas, trazas y logs centralizados. Los servicios exponen métricas para Prometheus, envían registros estructurados a Loki y publican trazas mediante OpenTelemetry hacia Jaeger. Esta combinación permite diagnosticar problemas en tiempo real.
En cada servicio se habilita el endpoint de métricas y se configura un scrape job en Prometheus.
scrape_configs:
- job_name: "tiendanova"
metrics_path: "/actuator/prometheus"
static_configs:
- targets:
- "catalog-service:8080"
- "order-service:8080"
- "payment-service:8080"
Las métricas clave (latencia, errores y tráfico) alimentan tableros de Grafana y alertas automáticas. Para los logs se utiliza un agente que envía los registros JSON a una instancia centralizada, lo que facilita correlacionar eventos entre servicios.
Los servicios se instrumentan con el SDK de OpenTelemetry, configurando un exportador OTLP y propagación de identificadores de trazas a través del gateway. De esta manera se pueden seguir las transacciones de compra de extremo a extremo y localizar cuellos de botella.