Saltar al contenido

Lección 20 de 20 · Lectura y práctica

Proyecto integrador: API de microservicios para e-commerce

Concepto claveEn el desarrollo de microservicios para e-commerce, la arquitectura debe balancear rendimiento, escalabilidad y mantenibilidad. Imagina un centro comercial donde cada tienda (microservicio) opera independientemente:.

Del concepto a la práctica

Taller de esta lección

El flujo integrador debe conservar una secuencia verificable: autenticar, consultar producto, comprobar stock y crear pedido. Usa datos pequeños: producto P-10 con precio unitario 12.50 y stock 4; el pedido de cantidad 2 debe tener importe 25.00 antes de impuestos o descuentos explícitos.

Ejemplo resuelto

La solicitud de pedido contiene product_id:"P-10" y quantity:2. El servicio de pedidos consulta el catálogo, valida que 2 sea positivo y no supere 4, calcula 2 × 12.50 = 25.00 y persiste el pedido solo después de superar esas comprobaciones.

Pruébalo

  1. Autentica un usuario local y conserva el token solo en memoria.

  2. Consulta P-10 y registra precio y stock devueltos.

  3. Crea el pedido de cantidad 2.

  4. Repite con cantidad 5 y con producto inexistente.

Error frecuente: descontar stock antes de validar todo el pedido o confiar en el precio enviado por el cliente. Corrígelo usando el catálogo como fuente del precio y una operación consistente para reservar existencias.

Comprueba tu resultado

El pedido válido debe registrar cantidad 2 e importe 25.00; el stock restante debe ser 2. La cantidad 5 y el producto inexistente deben fallar sin crear pedidos ni modificar stock.

Pedido atómico local y límite entre servicios

Usa céntimos enteros: P-10 unitario1250,stock4; qty2 produce2500 (25.00). La prueba local inicia de nuevo antes de cada fallo. Este repositorio completo solo muestra atomicidad en un proceso; requiere errors,sync importados.

go
type Store struct { mu sync.Mutex; Stock int; Orders []int64 }
func (s *Store) crear(productID string,qty int)(int64,error){
  s.mu.Lock();defer s.mu.Unlock()
  if productID!="P-10"{return 0,errors.New("producto inexistente")}
  if qty<=0||qty>s.Stock{return 0,errors.New("cantidad o stock inválido")}
  total:=int64(qty)*1250
  s.Stock-=qty;s.Orders=append(s.Orders,total)
  return total,nil
}

Fixture Store{Stock:4,Orders:make([]int64,0)}: crear(P-10,2)→2500,Stock2,len1. Desde otro Store stock4,qty5 falla con Stock4,len0; P-99/2 y qty0 también. No copies Store después de usar mutex. Autenticación/identidad se verifica antes de llamar crear; no confía en precio o user_id recibidos del cliente. Repetir la petición no es idempotente todavía: añade clave de pedido y registro del resultado para evitar duplicar reservas.

En microservicios el mutex del servicio de pedidos no hace atómica la reserva en catálogo ni persiste pedidos tras reiniciar. Usa comando de reserva, idempotencia y transición/compensación para coordinar fallos, o transacción del dueño de esos datos cuando corresponda. No declares un stock restante2 si solo consultaste stock y nunca lo reservaste. Si el catálogo está indisponible, no inventes disponibilidad desde caché obsoleta. Marca pruebas de DB, JWT, Docker y comunicación real como pendientes si no se ejecutaron.

Navegador, API y base de datos conectados por solicitudes, consultas y respuestas en sentidos opuestos.
CursaloMapa de práctica
Del navegador a los datos

El navegador envía una solicitud a la API. El servidor valida la operación y consulta los datos; después devuelve una respuesta que el navegador puede mostrar. La base de datos se comunica con el servidor y no directamente con el navegador.

Ver la infografía en tamaño completo ↗

Concepto clave

En el desarrollo de microservicios para e-commerce, la arquitectura debe balancear rendimiento, escalabilidad y mantenibilidad. Imagina un centro comercial donde cada tienda (microservicio) opera independientemente: la tienda de ropa no depende de la de electrónicos, pero ambas comparten servicios comunes como seguridad y limpieza (infraestructura). En nuestro proyecto integrador, construiremos una API para un sistema de e-commerce con tres microservicios principales: productos, pedidos y usuarios, cada uno con su propia base de datos y lógica de negocio, comunicándose a través de APIs REST.

La clave está en el desacoplamiento: cambios en el servicio de productos no deben afectar al de pedidos. Usaremos gorilla/mux como enrutador por su eficiencia en Go, ideal para APIs de alto rendimiento. Este enfoque permite escalar servicios individualmente según demanda (ej: más tráfico en productos durante rebajas) y desplegar actualizaciones sin downtime global.

Cómo funciona en la práctica

Vamos a implementar un flujo típico de compra en e-commerce. Paso 1: Un usuario se autentica en el servicio de usuarios (ej: POST /login). Paso 2: Busca productos en el servicio de productos (GET /products). Paso 3: Crea un pedido en el servicio de pedidos (POST /orders), que internamente valida el stock con el servicio de productos. Cada servicio corre en su propio contenedor Docker, con comunicación vía HTTP y manejo de errores como timeouts.

Para la comunicación entre servicios, evitaremos acoplamiento directo usando clientes HTTP con circuit breakers. Por ejemplo, si el servicio de productos está caído, el servicio de pedidos puede fallar rápido o usar una caché, en lugar de bloquearse. Desplegaremos todo con Docker Compose localmente, simulando un entorno de producción mínimo.

Código en acción

Configuración básica de un microservicio de productos con gorilla/mux:

package main

import (
    "encoding/json"
    "log"
    "net/http"
    "github.com/gorilla/mux"
)

type Product struct {
    ID    string  `json:"id"`
    Name  string  `json:"name"`
    Price float64 `json:"price"`
}

var products = []Product{
    {ID: "1", Name: "Laptop", Price: 999.99},
    {ID: "2", Name: "Mouse", Price: 25.50},
}

func getProducts(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(products)
}

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/products", getProducts).Methods("GET")
    log.Fatal(http.ListenAndServe(":8080", r))
}

Antes: Sin estructura clara, mezclando lógica. Después: Refactorizado para usar handlers separados y middleware de logging:

// handlers/product_handler.go
package handlers

import (
    "encoding/json"
    "net/http"
)

func GetProducts(w http.ResponseWriter, r *http.Request) {
    products := []Product{{ID: "1", Name: "Laptop", Price: 999.99}}
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(products)
}

// main.go
package main

import (
    "log"
    "net/http"
    "github.com/gorilla/mux"
    "tu-proyecto/handlers"
)

func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Printf("%s %s", r.Method, r.URL.Path)
        next.ServeHTTP(w, r)
    })
}

func main() {
    r := mux.NewRouter()
    r.Use(loggingMiddleware)
    r.HandleFunc("/products", handlers.GetProducts).Methods("GET")
    log.Fatal(http.ListenAndServe(":8080", r))
}

Errores comunes

  • Acoplamiento fuerte entre servicios: Llamar directamente a bases de datos de otros servicios. Solución: Usar APIs REST y manejar fallos con retries o circuit breakers.
  • Falta de manejo de timeouts: No configurar timeouts en clientes HTTP, causando bloqueos. Solución: Usar context.WithTimeout en Go para limitar llamadas.
  • Ignorar versionado de APIs: Cambiar endpoints sin versionar, rompiendo clientes. Solución: Usar rutas como /v1/products y planificar migraciones.
  • Despliegue monolítico: Correr todos los servicios en un solo contenedor, perdiendo beneficios de microservicios. Solución: Usar Docker Compose con servicios separados.
  • Logs desestructurados: Imprimir logs sin formato, dificultando monitoreo. Solución: Usar JSON logging con campos como timestamp y service_name.

Checklist de dominio

  1. ¿Puedes crear un microservicio con gorilla/mux que responda a GET y POST?
  2. ¿Sabes configurar Dockerfiles para cada servicio y orquestarlos con Docker Compose?
  3. ¿Implementas comunicación entre servicios con clientes HTTP y manejo de errores?
  4. ¿Usas middleware para logging y autenticación en tus rutas?
  5. ¿Versionas tus APIs para mantener compatibilidad?
  6. ¿Pruebas tus servicios individualmente y en integración?
  7. ¿Documentas tus endpoints con ejemplos de request/response?

Implementa un microservicio de carrito de compras

Extiende el proyecto de e-commerce agregando un servicio de carrito que permita a usuarios gestionar productos antes de comprar. Sigue estos pasos:

  1. Crea un nuevo directorio cart-service con su propio main.go y Dockerfile.
  2. Define una estructura Cart con campos: ID (string), UserID (string), Items (slice de ProductID y Quantity).
  3. Implementa endpoints REST usando gorilla/mux:
    • POST /cart: Crea un carrito para un usuario (recibe UserID en JSON).
    • GET /cart/{userId}: Obtiene el carrito de un usuario.
    • PUT /cart/{userId}/items: Agrega o actualiza items (recibe ProductID y Quantity en JSON).
    • DELETE /cart/{userId}: Elimina el carrito.
  4. Agrega logging middleware que registre cada request.
  5. Integra este servicio en tu docker-compose.yml, exponiendo el puerto 8082.
  6. Prueba la comunicación: Desde el servicio de pedidos, llama a GET /cart/{userId} para validar items antes de crear un pedido.

Entrega: Código fuente en GitHub y captura de pantalla de Docker Compose corriendo.

Pistas
  • Usa un map en memoria para almacenar carritos por simplicidad, pero documenta que en producción necesitarías una base de datos.
  • Para el middleware, recuerda que gorilla/mux permite r.Use() global o por ruta.
  • En Docker Compose, define networks para que los servicios se comuniquen por nombre (ej: cart-service:8082).

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.

Sigue con Go con Gorilla/Mux: CRUD, Validación y Microservicios

Esta lección forma parte de un curso de Cursalo. Revisa su programa y elige entre la compra individual o el acceso a toda la escuela.

Si ya tenías acceso a este curso, entra con tu cuenta: tus derechos y tu progreso se conservan. No necesitas comprarlo de nuevo.

01 / ELEGIR UNA RUTA

El próximo paso

Aprende IA mientras construyes algo que sí necesitas.

Empieza por el curso inicial o explora el catálogo para encontrar una práctica alineada con tu trabajo.