Sistema de e-commerce distribuido implementando el patrón Saga con Orquestación para manejo de transacciones distribuidas.
- Arquitectura del Sistema
- Tecnologías Utilizadas
- Microservicios
- Traefik - Reverse Proxy
- Patrón Saga
- Requisitos Previos
- Instalación
- Configuración
- Ejecución
- API Endpoints
- Testing
- Troubleshooting
El sistema está compuesto por 5 microservicios independientes que se comunican a través de HTTP REST APIs mediante Traefik como Reverse Proxy, coordinados por un orquestador Saga:
┌─────────────────────────────────────┐
│ TRAEFIK │
│ Reverse Proxy │
│ Puerto: 80 │
│ Dashboard: 8080 │
└───────────────┬─────────────────────┘
│
┌────────────────────────────────┼────────────────────────────────┐
│ │ │
▼ ▼ ▼
/api/catalog /api/saga /api/payments
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌────────────────────┐ ┌──────────────┐
│ CATALOG │ │ ORCHESTRATOR │ │ PAYMENTS │
│ (Django) │ │ (FastAPI) │ │ (Django) │
│ Puerto:8001 │ │ Puerto: 8000 │ │ Puerto:8002 │
│ │ │ │ │ │
│ • Productos │ │ • Coordina Saga │ │ • Pagos │
│ • Siempre OK │ │ • Compensaciones │ │ • Reembolsos │
└──────────────┘ └────────────────────┘ │ • 5% fallo │
└──────────────┘
┌────────────────────────────────┼────────────────────────────────┐
│ │
▼ ▼
/api/inventory /api/purchases
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ INVENTORY │ │ PURCHASES │
│ (Django) │ │ (Django) │
│ Puerto:8003 │ │ Puerto:8004 │
│ │ │ │
│ • Stock │ │ • Compras │
│ • 30% fallo │ │ • Cancela. │
│ • Compensar │ │ • 5% fallo │
└──────────────┘ └──────────────┘
│ │
└─────────────────────────┬───────────────────────────────────────┘
│
▼
┌──────────────────────────────┐
│ PostgreSQL │
│ │
│ • ms_catalog (Productos) │
│ • ms_payments (Pagos) │
│ • ms_inventory (Inventario) │
│ • ms_purchases (Compras) │
└──────────────────────────────┘
1. Cliente → POST /api/saga/transaction → Traefik → Orchestrator
2. Orchestrator → GET /api/catalog/products/random/ → Traefik → Catalog (siempre éxito)
3. Orchestrator → POST /api/payments/payments/ → Traefik → Payments (5% fallo)
4. Si Payment OK → POST /api/inventory/inventory/decrease/ → Traefik → Inventory (30% fallo)
5. Si Inventory OK → POST /api/purchases/purchases/ → Traefik → Purchases (5% fallo)
6. Si todo OK → TRANSACTION COMPLETED ✅
En caso de fallo en cualquier paso:
- COMPENSACIÓN: Orchestrator ejecuta rollback en orden reverso:
1. DELETE /api/purchases/purchases/{id}/cancel/ (si se creó)
2. POST /api/inventory/inventory/compensate/ (si se decrementó)
3. POST /api/payments/payments/{id}/refund/ (si se creó)
- Python 3.14 - Lenguaje de programación
- Django 5.2 - Framework web para catalog, payments, inventory, purchases
- Django REST Framework 3.15 - API REST para microservicios Django
- FastAPI 0.115 - Framework asíncrono para orchestrator
- Pydantic - Validación de datos en FastAPI
- PostgreSQL 17 - Base de datos relacional
- psycopg2 - Adaptador PostgreSQL para Python
- Docker 24+ - Contenedorización
- Docker Compose - Orquestación de contenedores
- Traefik v3 - Reverse Proxy y Load Balancer
- uv - Gestor de paquetes Python (ultra-rápido)
- Gunicorn - WSGI server para Django
- Uvicorn - ASGI server para FastAPI
- Ruff - Linter y formatter Python
- pytest - Framework de testing
- httpx - Cliente HTTP asíncrono
Tecnología: FastAPI
Responsabilidad: Coordinar transacciones distribuidas mediante Saga Pattern
- Orquesta el flujo completo de transacciones
- Implementa lógica de compensación (rollback)
- Mantiene estado de todas las transacciones en memoria
- No tiene base de datos propia (stateless por diseño)
POST /saga/transaction- Iniciar transacción distribuidaGET /saga/status/{transaction_id}- Consultar estado de transacciónGET /saga/transactions- Listar todas las transacciones
orchestrator/
├── app/
│ ├── config.py # Configuración (URLs de servicios)
│ ├── main.py # FastAPI app
│ ├── models.py # Modelos Pydantic
│ ├── routes/
│ │ └── saga_routes.py # Endpoints REST
│ ├── services/
│ │ ├── compensation.py # Lógica de compensación
│ │ ├── http_client.py # Cliente HTTP para servicios
│ │ └── saga_service.py # Lógica principal del Saga
│ └── storage/
│ └── transaction_store.py # Almacenamiento en memoria
├── Dockerfile
└── pyproject.toml
# Los servicios se comunican a través de Traefik
CATALOG_URL = "http://traefik/api/catalog"
PAYMENTS_URL = "http://traefik/api/payments"
INVENTORY_URL = "http://traefik/api/inventory"
PURCHASES_URL = "http://traefik/api/purchases"Tecnología: Django + Django REST Framework
Responsabilidad: Proveer productos aleatorios para el Saga
- Siempre retorna 200 OK (no falla nunca)
- Genera productos aleatorios si no existen en DB
- Simula latencia de 0.1 a 0.5 segundos
- NO requiere compensación (solo lectura)
GET /api/catalog/health/- Health checkGET /api/catalog/products/random/- Obtener producto aleatorio (siempre éxito)
class Product(models.Model):
name = models.CharField(max_length=255)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
category = models.CharField(max_length=100)
stock = models.IntegerField(default=0)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True){
"product_id": 1,
"name": "Product-9377",
"description": "Random product description 35",
"price": "491.80",
"category": "Electronics",
"stock": 76
}- Nombre:
ms_catalog - Tabla:
products
Tecnología: Django + Django REST Framework
Responsabilidad: Procesar pagos y manejar reembolsos
- 5% de probabilidad de fallo aleatorio en creación de pagos
- Retorna 200 (éxito) o 409 (conflicto/error)
- Implementa endpoint de compensación (refund)
- Simula latencia de 0.5 a 2 segundos
GET /api/payments/payments/health/- Health checkPOST /api/payments/payments/- Crear pago (5% fallo aleatorio)POST /api/payments/payments/{id}/refund/- Reembolsar pago (compensación)
class Payment(models.Model):
user_id = models.CharField(max_length=100)
transaction_id = models.CharField(max_length=100)
product_id = models.CharField(max_length=100, null=True)
amount = models.DecimalField(max_digits=10, decimal_places=2)
status = models.CharField(max_length=20) # success, error, compensated
created_at = models.DateTimeField(auto_now_add=True)
compensated_at = models.DateTimeField(null=True, blank=True){
"user_id": "user-123",
"transaction_id": "txn-456",
"amount": 99.99
}{
"payment_id": 42,
"status": "success",
"message": "Payment processed successfully",
"transaction_id": "txn-456",
"user_id": "user-123",
"product_id": null
}{
"payment_id": 43,
"status": "error",
"message": "Error processing payment",
"transaction_id": "txn-457",
"user_id": "user-123",
"product_id": null
}- Nombre:
ms_payments - Tabla:
app_payment
Tecnología: Django + Django REST Framework
Responsabilidad: Gestionar inventario y decrementar stock
- 30% de probabilidad de fallo aleatorio (simula falta de stock)
- Retorna 200 (éxito) o 409 (stock insuficiente)
- Implementa endpoint de compensación para restaurar stock
- Simula latencia de 0.1 a 0.8 segundos
GET /api/inventory/health/- Health checkPOST /api/inventory/inventory/decrease/- Decrementar inventario (30% fallo)POST /api/inventory/inventory/compensate/- Restaurar inventario (compensación)
class Inventory(models.Model):
product_id = models.CharField(max_length=100, unique=True)
quantity = models.IntegerField(default=0)
last_updated = models.DateTimeField(auto_now=True)
created_at = models.DateTimeField(auto_now_add=True){
"product_id": "1",
"quantity": 2,
"transaction_id": "txn-456"
}{
"status": "success",
"message": "Inventory decreased successfully",
"product_id": "1",
"remaining_quantity": 98
}{
"status": "error",
"message": "Insufficient stock for product 1 (random failure)"
}- Nombre:
ms_inventory - Tablas:
inventory_inventory,inventory_inventoryoperation
Tecnología: Django + Django REST Framework
Responsabilidad: Registrar compras y manejar cancelaciones
- 5% de probabilidad de fallo aleatorio en creación
- Retorna 201 (éxito) o 409 (conflicto)
- Implementa endpoint de compensación (cancel)
- Simula latencia de 0.05 a 0.2 segundos
GET /api/purchases/health/- Health checkPOST /api/purchases/purchases/- Crear compra (5% fallo aleatorio)DELETE /api/purchases/purchases/{transaction_id}/cancel/- Cancelar compra (compensación)
class Purchase(models.Model):
user_id = models.CharField(max_length=100)
transaction_id = models.CharField(max_length=100)
product_id = models.CharField(max_length=100)
payment_id = models.CharField(max_length=100)
amount = models.DecimalField(max_digits=10, decimal_places=2)
status = models.CharField(max_length=20) # success, cancelled
created_at = models.DateTimeField(auto_now_add=True)
cancelled_at = models.DateTimeField(null=True, blank=True){
"user_id": "user-123",
"transaction_id": "txn-456",
"product_id": "1",
"payment_id": "42",
"amount": 99.99
}{
"purchase_id": 15,
"status": "success",
"user_id": "user-123",
"transaction_id": "txn-456",
"product_id": "1",
"payment_id": "42",
"amount": "99.99"
}{
"status": "error",
"message": "Purchase failed",
"error": "CONFLICT"
}- Nombre:
ms_purchases - Tabla:
app_purchase
Traefik es un reverse proxy y load balancer moderno diseñado específicamente para microservicios. En este proyecto, Traefik actúa como puerta de entrada única que enruta las peticiones a los microservicios correspondientes.
Traefik se despliega de forma externa y los servicios se conectan a la red traefik-public. El puerto 80 es el punto de entrada único al sistema.
- ✅ Auto-descubrimiento: Detecta automáticamente los contenedores Docker
- ✅ Routing dinámico: Enruta peticiones según etiquetas Docker
- ✅ Strip Prefix: Elimina el prefijo
/api/serviceantes de enviar al servicio - ✅ Health checks: Monitorea la salud de los servicios
- ✅ Dashboard: Interfaz web para visualizar rutas y servicios (puerto 8080)
- ✅ HTTPS/TLS: Soporte para certificados SSL (configurable)
Internet/Cliente
│
▼
┌───────────────┐
│ TRAEFIK │
│ Puerto: 80 │
│ (Dashboard: │
│ 8080) │
└───────┬───────┘
│
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
/api/catalog /api/saga /api/payments
│ │ │
▼ ▼ ▼
catalog:8001 orchestrator:8000 payments:8002
▼ ▼
/api/inventory /api/purchases
│ │
▼ ▼
inventory:8003 purchases:8004
Traefik enruta las peticiones según el path y elimina el prefijo antes de enviar al servicio:
| Path Original | Se transforma en | Servicio |
|---|---|---|
http://localhost/api/saga/* |
/saga/* |
Orchestrator |
http://localhost/api/catalog/* |
/* |
Catalog |
http://localhost/api/payments/* |
/* |
Payments |
http://localhost/api/inventory/* |
/* |
Inventory |
http://localhost/api/purchases/* |
/* |
Purchases |
Cada servicio tiene etiquetas que Traefik lee para configurar el enrutamiento:
# Ejemplo: Catalog
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik-public"
- "traefik.http.routers.catalog.rule=PathPrefix(`/api/catalog`)"
- "traefik.http.routers.catalog.entrypoints=web"
- "traefik.http.services.catalog.loadbalancer.server.port=8001"
- "traefik.http.middlewares.catalog-strip.stripprefix.prefixes=/api/catalog"
- "traefik.http.routers.catalog.middlewares=catalog-strip"Explicación:
traefik.enable=true- Habilita Traefik para este serviciotraefik.docker.network=traefik-public- Red Docker a usarPathPrefix(/api/catalog)- Coincide con URLs que empiezan con /api/catalogstripprefix.prefixes=/api/catalog- Elimina el prefijo antes de enviar al servicioloadbalancer.server.port- Puerto interno del servicio
Accede al dashboard en: http://localhost:8080
El dashboard muestra:
- ✅ Servicios activos y su estado
- ✅ Routers configurados
- ✅ Middlewares aplicados
- ✅ Health checks en tiempo real
# PowerShell
Invoke-RestMethod -Uri "http://localhost/api/saga/transaction" -Method POST -ContentType "application/json" -Body '{"user_id": "1", "amount": 100}'# Bash/curl
curl -X POST http://localhost/api/saga/transaction \
-H "Content-Type: application/json" \
-d '{"user_id": "1", "amount": 100}'# Health del saga
curl http://localhost/api/saga/
# Health de catalog
curl http://localhost/api/catalog/health/
# Health de payments
curl http://localhost/api/payments/payments/health/
# Health de inventory
curl http://localhost/api/inventory/health/
# Health de purchases
curl http://localhost/api/purchases/health/- Punto de Entrada Único: Un solo puerto (80) para todos los servicios
- Simplicidad: No necesitas recordar múltiples puertos
- Producción-Ready: Listo para añadir HTTPS con Let's Encrypt
- Load Balancing: Puede distribuir carga entre múltiples instancias
- Service Discovery: Descubre servicios automáticamente
- Observabilidad: Dashboard para monitoreo en tiempo real
Para habilitar HTTPS en producción, agrega estas etiquetas:
labels:
- "traefik.http.routers.orchestrator.tls=true"
- "traefik.http.routers.orchestrator.tls.certresolver=letsencrypt"Y configura el cert resolver en el servicio de Traefik:
command:
- "--certificatesresolvers.letsencrypt.acme.email=tu-email@ejemplo.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"Traefik monitorea automáticamente la salud de cada servicio usando los health checks definidos en Docker Compose:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8001/health/"]
interval: 10s
timeout: 5s
retries: 3
start_period: 30sSi un servicio falla el health check, Traefik automáticamente deja de enviarle tráfico.
Si prefieres acceso directo a los servicios sin Traefik:
- Comenta la sección de Traefik en
docker-compose.prod.yml - Expone los puertos directamente en cada servicio:
# En cada servicio
ports:
- "8001:8001" # catalog
- "8002:8002" # payments
# etc...- Elimina las labels de Traefik de los servicios
El patrón Saga es un patrón de diseño para manejar transacciones distribuidas en arquitecturas de microservicios, donde no es posible usar transacciones ACID tradicionales.
Este proyecto implementa Saga con Orquestación:
- ✅ El Orchestrator coordina toda la transacción
- ✅ Comunica directamente con cada microservicio vía HTTP
- ✅ Ejecuta compensaciones en orden reverso si hay fallo
- ✅ Mantiene estado completo de cada transacción
Cuando una transacción falla, el orchestrator ejecuta compensaciones en orden reverso:
Creación (orden forward):
1. Catalog → OK
2. Payment → OK
3. Inventory → FAIL ❌
Compensación (orden reverso):
1. Purchase → (no se creó, skip)
2. Inventory → COMPENSATE (restaurar stock) ✅
3. Payment → REFUND ✅
COMPLETED- Transacción exitosa (todos los pasos OK)COMPENSATED- Transacción fallida y revertida
Dado que cada servicio tiene diferentes tasas de fallo (excepto catalog que siempre es OK):
- Payments: 95% éxito
- Inventory: 70% éxito
- Purchases: 95% éxito
Probabilidad de éxito total: 0.95 × 0.70 × 0.95 = 63.2%
Probabilidad de fallo: 36.8%
-
Docker 24.0 o superior
docker --version # Docker version 24.0.0 o superior -
Docker Compose 2.20 o superior
docker compose version # Docker Compose version v2.20.0 o superior -
Git
git --version # git version 2.30.0 o superior -
(Opcional) curl - Para testing de APIs
curl --version
-
(Opcional) jq - Para formatear JSON
jq --version
- RAM: Mínimo 4 GB (Recomendado 8 GB)
- Disco: Mínimo 5 GB libres
- CPU: 2 cores mínimo
- Puertos disponibles: 80 (Traefik), 8080 (Traefik Dashboard)
El proyecto requiere una red externa de Docker llamada traefik-public. Si no existe, créala:
docker network create traefik-publicgit clone https://github.com/Zapallo-Code/ecommerce-microservices.git
cd ecommerce-microservicestree -L 2 -dDeberías ver:
.
├── catalog/
│ ├── main/
│ └── products/
├── inventory/
│ ├── config/
│ └── inventory/
├── orchestrator/
│ └── app/
├── payments/
│ ├── app/
│ └── main/
├── purchases/
│ ├── app/
│ └── main/
└── scripts/
docker-compose.yml- Para desarrollodocker-compose.prod.yml- Para producción (recomendado)- Cada microservicio tiene su
Dockerfile
El proyecto incluye archivos de ejemplo para configurar las variables de entorno:
.env.example- Plantilla para desarrollo.env.example.prod- Plantilla para producción
# Desarrollo
cp .env.example .env
# Producción
cp .env.example.prod .env.prod| Variable | Descripción | Valor por defecto |
|---|---|---|
POSTGRES_USER |
Usuario PostgreSQL | postgres |
POSTGRES_PASSWORD |
Contraseña PostgreSQL | postgres |
CATALOG_SECRET_KEY |
Secret key Django Catalog | (generado) |
PAYMENTS_SECRET_KEY |
Secret key Django Payments | (generado) |
INVENTORY_SECRET_KEY |
Secret key Django Inventory | (generado) |
PURCHASES_SECRET_KEY |
Secret key Django Purchases | (generado) |
LOG_LEVEL |
Nivel de logging | INFO |
CORS_ALLOWED_ORIGINS |
Orígenes CORS permitidos | * |
python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'- Crear la red de Traefik (si no existe):
docker network create traefik-public- Tener Traefik corriendo (ver sección Traefik más abajo)
# Copiar configuración
cp .env.example .env
# Iniciar servicios
docker compose up --build -d
# Ver logs
docker compose logs -f# Copiar configuración de producción
cp .env.example.prod .env.prod
# Editar .env.prod con valores seguros
# nano .env.prod
# Iniciar servicios
docker compose -f docker-compose.prod.yml --env-file .env.prod up --build -dExplicación de flags:
-f docker-compose.prod.yml- Usa el archivo de producción--env-file .env.prod- Usa variables de entorno de producción--build- Reconstruye las imágenes-d- Modo detached (background)
# Desarrollo
docker compose ps
# Producción
docker compose -f docker-compose.prod.yml psDeberías ver todos los servicios con estado healthy:
NAME STATUS
ecommerce-catalog Up (healthy)
ecommerce-inventory Up (healthy)
ecommerce-orchestrator Up (healthy)
ecommerce-payments Up (healthy)
ecommerce-postgres Up (healthy)
ecommerce-purchases Up (healthy)
# Logs del orchestrator
docker logs ecommerce-orchestrator -f
# Logs de payments
docker logs ecommerce-payments -f
# Logs de todos los servicios
docker compose logs -f# Desarrollo
docker compose down
# Producción
docker compose -f docker-compose.prod.yml downdocker compose -f docker-compose.prod.yml down -vSi no tienes Traefik instalado, puedes usar el siguiente docker-compose:
# traefik-compose.yml
services:
traefik:
image: traefik:v3.0
container_name: traefik
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- traefik-public
networks:
traefik-public:
external: truedocker compose -f traefik-compose.yml up -dPara verificar que el sistema funciona correctamente:
# PowerShell - Health check del saga
Invoke-RestMethod -Uri "http://localhost/api/saga/"# Bash - Health check del saga
curl http://localhost/api/saga/Ejecutar una transacción de prueba:
# PowerShell
Invoke-RestMethod -Uri "http://localhost/api/saga/transaction" -Method POST -ContentType "application/json" -Body '{"user_id": "1", "amount": 100}'# Bash
curl -X POST http://localhost/api/saga/transaction \
-H "Content-Type: application/json" \
-d '{"user_id": "1", "amount": 100}'Deberías recibir una respuesta con status: "COMPLETED" o status: "FAILED" (con compensaciones ejecutadas).
Las migraciones se crean automáticamente al iniciar los contenedores, pero si necesitas recrearlas:
# Catalog
docker exec ecommerce-catalog python manage.py makemigrations products
docker exec ecommerce-catalog python manage.py migrate
# Payments
docker exec ecommerce-payments python manage.py makemigrations app
docker exec ecommerce-payments python manage.py migrate
# Inventory
docker exec ecommerce-inventory python manage.py makemigrations inventory
docker exec ecommerce-inventory python manage.py migrate
# Purchases
docker exec ecommerce-purchases python manage.py makemigrations app
docker exec ecommerce-purchases python manage.py migrateEste es el único endpoint que necesitas para probar el sistema completo:
URL: http://localhost/api/saga/transaction
Request Body:
{
"user_id": "1",
"amount": 100
}Ejemplo con PowerShell:
Invoke-RestMethod -Uri "http://localhost/api/saga/transaction" -Method POST -ContentType "application/json" -Body '{"user_id": "1", "amount": 100}'Ejemplo con curl:
curl -X POST http://localhost/api/saga/transaction \
-H "Content-Type: application/json" \
-d '{"user_id": "1", "amount": 100}'Respuesta Exitosa (COMPLETED):
{
"transaction_id": "e8065740-3844-4026-b366-be1d15580f64",
"status": "COMPLETED",
"steps": {
"catalog": {"status": "success", "product": {...}},
"payment": {"status": "success", "payment_id": 35},
"inventory": {"status": "success"},
"purchase": {"status": "success", "purchase_id": 12}
},
"timestamp": "2025-12-01T04:09:06.888287"
}Respuesta con Fallo (FAILED - con compensación):
{
"transaction_id": "24f59cbe-0b89-427c-8180-2c3b6c8967b8",
"status": "FAILED",
"failed_step": "inventory",
"error": "Sin stock disponible",
"compensations": ["payment refunded"],
"timestamp": "2025-12-01T04:06:24.487881"
}curl http://localhost/api/saga/Respuesta:
{
"status": "healthy",
"service": "saga-orchestrator"
}curl http://localhost/api/saga/status/e8065740-3844-4026-b366-be1d15580f64curl http://localhost/api/saga/transactionscurl http://localhost/api/catalog/products/random/Respuesta (siempre 200):
{
"product_id": 1,
"name": "Product-9377",
"description": "Random product description 35",
"price": "491.80",
"category": "Electronics",
"stock": 76
}curl -X POST http://localhost/api/payments/payments/ \
-H "Content-Type: application/json" \
-d '{
"user_id": "user-123",
"transaction_id": "txn-456",
"amount": 99.99
}'curl -X POST http://localhost/api/payments/payments/42/refund/curl -X POST http://localhost/api/inventory/inventory/decrease/ \
-H "Content-Type: application/json" \
-d '{
"product_id": 1,
"quantity": 2,
"operation_id": "550e8400-e29b-41d4-a716-446655440000"
}'curl -X POST http://localhost/api/inventory/inventory/compensate/ \
-H "Content-Type: application/json" \
-d '{
"operation_id": "550e8400-e29b-41d4-a716-446655440000"
}'curl -X POST http://localhost/api/purchases/purchases/ \
-H "Content-Type: application/json" \
-d '{
"user_id": "user-123",
"transaction_id": "txn-456",
"product_id": "1",
"payment_id": "42",
"amount": 99.99
}'curl -X DELETE http://localhost/api/purchases/purchases/txn-456/cancel/- Orchestrator (FastAPI): Punto de entrada único para clientes
- Catalog (Django): Lógica de productos
- Payments (Django): Lógica de pagos
- Inventory (Django): Lógica de inventario
- Purchases (Django): Lógica de compras
- PostgreSQL: 4 bases de datos independientes
- In-Memory Store: Estado de transacciones en Orchestrator
Todos los servicios se comunican a través de Traefik:
HTTP REST (Síncrono via Traefik)
┌─────────────┐
│ Orchestrator│
└──────┬──────┘
│
└─── http://traefik/api/* ───→ Traefik ───→ Services
El orchestrator usa las siguientes URLs para comunicarse:
http://traefik/api/catalog→ Catalog servicehttp://traefik/api/payments→ Payments servicehttp://traefik/api/inventory→ Inventory servicehttp://traefik/api/purchases→ Purchases service
┌──────────────────────────────────────┐
│ Transaction Store (In-Memory) │
├──────────────────────────────────────┤
│ { │
│ "transaction_id": "uuid", │
│ "status": "COMPLETED|COMPENSATED", │
│ "payment_id": "...", │
│ "inventory_updated": true|false, │
│ "purchase_registered": true|false, │
│ "error_message": "...", │
│ "created_at": "timestamp", │
│ "completed_at": "timestamp" │
│ } │
└──────────────────────────────────────┘
- Health Checks: Cada servicio expone
/health/ - Timeouts: Configurados en HTTP client del orchestrator
- Retry Logic: No implementado (fallo = compensación inmediata)
- Compensación Idempotente: Compensaciones pueden ejecutarse múltiples veces
Este es un proyecto académico para demostrar el patrón Saga en microservicios.
git commit -m "tipo: descripción
- Detalle 1
- Detalle 2"Tipos:
feat: Nueva funcionalidadfix: Corrección de bugdocs: Cambios en documentaciónrefactor: Refactorización de códigotest: Añadir o modificar testschore: Tareas de mantenimiento
Este proyecto está bajo la Licencia MIT. Ver archivo LICENSE para más detalles.
- Valentin Rubio
- Luciano Castro
- Santiago Oses
- Santiago Calzolari
- Pablo Geyer