Este proyecto es un microservicio de ejemplo desarrollado en Python con Flask, diseñado para ilustrar la implementación de patrones de arquitectura orientada a servicios (SOA) en un contexto práctico de retail.
El servicio expone un único endpoint para calcular el precio final de un producto, aplicando reglas de negocio como descuentos por tipo de cliente y validación de cupones.
El código está estructurado para demostrar los siguientes principios de SOA:
-
Canonical Schema (Esquema Canónico): El servicio no opera directamente con los identificadores de producto de los sistemas de origen (ej:
P12345del e-commerce o789123del POS). En su lugar, los transforma a un formato canónico (SKU-WEB-001,SKU-LEGACY-ABC) antes de procesarlos. Esto se simula enserver.pycon la funcióntransformar_a_canonico. -
Logic Centralization (Centralización de la Lógica): Las reglas de negocio complejas o que cambian con frecuencia (como la validación de un cupón) no están codificadas directamente en el servicio principal. En su lugar, se delegan a un componente especializado (
ReglasNegocioClient), que simula una llamada a un motor de reglas externo. -
Service Normalization (Normalización del Servicio): La clase
PreciosPromocionesServiceactúa como una fachada unificada. Consolida la lógica de orquestación (obtener precio base, consultar reglas, aplicar descuentos) en una única interfaz cohesiva, independientemente de la complejidad de las dependencias subyacentes. -
Inyección de Dependencias: El
PreciosPromocionesServiceno crea sus propias dependencias (comoReglasNegocioClient). En su lugar, las recibe en su constructor. Este desacoplamiento es clave para la mantenibilidad y, especialmente, para la capacidad de prueba, ya que permite "inyectar" versiones falsas (mocks) de las dependencias durante las pruebas unitarias.
omniRetail-precio-service/
├── services/
│ ├── __init__.py
│ └── PreciosPromocionesService.py # Lógica de negocio principal
├── tests/
│ ├── __init__.py
│ └── test_precios_promociones_service.py # Pruebas unitarias del servicio
├── .gitignore
├── README.md
├── requirements.txt # Dependencias del proyecto
├── server.py # Servidor Flask y endpoint API
└── venv/ # Entorno virtual (ignorado por Git)
- Python 3.8+
pipyvenv
-
Clona el repositorio:
git clone <URL_DEL_REPOSITORIO> cd omniRetail-precio-service
-
Crea y activa un entorno virtual:
# En macOS/Linux python3 -m venv venv source venv/bin/activate # En Windows python -m venv venv .\venv\Scripts\activate
-
Instala las dependencias:
pip install -r requirements.txt
Para iniciar el servidor web de Flask, ejecuta el siguiente comando desde la raíz del proyecto:
python server.pyEl servicio estará disponible en http://127.0.0.1:5000.
Para verificar que la lógica del servicio funciona como se espera, puedes ejecutar las pruebas unitarias con pytest:
pytest -vVerás una salida que confirma que todas las pruebas han pasado.
El servicio expone un único endpoint para calcular precios.
- Endpoint:
POST /api/precios/calcular - Content-Type:
application/json
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
id_producto |
String | Sí | El identificador del producto (ej: "P12345"). |
cliente_tipo |
String | Sí | El tipo de cliente (ej: "VIP", "Regular"). |
cupon |
String | No | El código del cupón de descuento a aplicar. |
1. Cliente VIP con cupón válido:
curl -X POST http://127.0.0.1:5000/api/precios/calcular \
-H "Content-Type: application/json" \
-d '{
"id_producto": "P12345",
"cliente_tipo": "VIP",
"cupon": "20OFFELEC"
}'Respuesta Esperada (descuento VIP del 5% + descuento del cupón del 20% sobre el precio base de 100):
{
"mensaje": "Cálculo exitoso y reglas aplicadas.",
"precio_final": 75.00,
"sku_procesado": "SKU-WEB-001"
}2. Producto no encontrado:
curl -X POST http://127.0.0.1:5000/api/precios/calcular \
-H "Content-Type: application/json" \
-d '{"id_producto": "ID_INEXISTENTE", "cliente_tipo": "Regular"}'Respuesta Esperada:
{
"error": "Producto con SKU canónico 'ID_INEXISTENTE' no encontrado."
}