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
Autentica un usuario local y conserva el token solo en memoria.
Consulta P-10 y registra precio y stock devueltos.
Crea el pedido de cantidad 2.
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.
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.

Mapa de prácticaEl 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
- ¿Puedes crear un microservicio con gorilla/mux que responda a GET y POST?
- ¿Sabes configurar Dockerfiles para cada servicio y orquestarlos con Docker Compose?
- ¿Implementas comunicación entre servicios con clientes HTTP y manejo de errores?
- ¿Usas middleware para logging y autenticación en tus rutas?
- ¿Versionas tus APIs para mantener compatibilidad?
- ¿Pruebas tus servicios individualmente y en integración?
- ¿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:
- Crea un nuevo directorio cart-service con su propio main.go y Dockerfile.
- Define una estructura Cart con campos: ID (string), UserID (string), Items (slice de ProductID y Quantity).
- 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.
- Agrega logging middleware que registre cada request.
- Integra este servicio en tu docker-compose.yml, exponiendo el puerto 8082.
- 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.