https://github.com/raulrobinson/idempotency-lab-opensource
Laboratorio basico que permite demostrar y comprobar el patron de idempotencia en las operaciones
https://github.com/raulrobinson/idempotency-lab-opensource
idempotency java laboratory-exercises mongodb nodejs opensource reactive webflux
Last synced: 10 days ago
JSON representation
Laboratorio basico que permite demostrar y comprobar el patron de idempotencia en las operaciones
- Host: GitHub
- URL: https://github.com/raulrobinson/idempotency-lab-opensource
- Owner: raulrobinson
- License: mit
- Created: 2026-06-07T15:56:13.000Z (21 days ago)
- Default Branch: main
- Last Pushed: 2026-06-07T17:35:46.000Z (21 days ago)
- Last Synced: 2026-06-07T19:19:32.355Z (21 days ago)
- Topics: idempotency, java, laboratory-exercises, mongodb, nodejs, opensource, reactive, webflux
- Language: Java
- Homepage:
- Size: 71.3 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# Laboratorio Basico de Idempotencia










### Introducción:
Este laboratorio implementa un patrón de idempotencia independiente del dominio.
1. El cliente envía una petición HTTP con un Idempotency-Key;
2. API Gateway la enruta hacia un servicio WebFlux encargado de controlar si la operación debe ejecutarse, bloquearse o responderse desde una respuesta previamente almacenada.
3. El servicio de idempotencia válida la clave, calcula un hash del payload y consulta MongoDB para identificar el estado de la petición.
4. Si la clave no existe, registra la operación como PROCESSING, invoca el servicio de dominio implementado en Node.js y guarda la respuesta final como COMPLETED.
5. Si la misma petición se repite con la misma clave y el mismo payload, el sistema no vuelve a ejecutar el dominio, sino que retorna la respuesta cacheada.
6. Si la clave se reutiliza con un payload diferente, el sistema responde con error 422.
7. Si otra petición con la misma clave aún está en proceso, responde con 409.
8. De esta forma, el componente evita duplicidad de operaciones, mantiene consistencia y puede reutilizarse para distintos casos de negocio sin depender de la lógica interna del dominio.
---
- Diagrama General:
```
Cliente
↓
Spring Cloud Gateway
↓
Servicio de Idempotencia
↓ ↓
MongoDB Lambda Node.js
```
---
- Diagrama de Flujo:
```mermaid
flowchart LR
CLIENT[Cliente]
GATEWAY[Spring Cloud Gateway
API Gateway]
IDEM[Spring WebFlux
Idempotency Service]
MONGO[(MongoDB
Idempotency Store)]
DOMAIN[Lambda Node.js
Servicio de Dominio]
CLIENT --> GATEWAY
GATEWAY --> IDEM
IDEM -->|consulta / guarda estado| MONGO
IDEM -->|solo si es nueva petición| DOMAIN
DOMAIN --> IDEM
IDEM --> GATEWAY
GATEWAY --> CLIENT
```
---
- Diagrama de Secuencia:
```mermaid
sequenceDiagram
autonumber
actor Client as Cliente / Postman
participant GW as Spring Cloud Gateway
:8080
participant API as Idempotency Controller
Spring WebFlux :8081
participant IDEM as Idempotency Service
participant MONGO as MongoDB
idempotency_records
participant NODE as Lambda Node.js
Domain Handler :3001
Client->>GW: POST /api/execute
Idempotency-Key: order-001
JSON Payload
GW->>GW: RewritePath
/api/execute -> /idempotency/execute
GW->>API: POST /idempotency/execute
Idempotency-Key + Payload
API->>IDEM: handle(idempotencyKey, payload)
alt Idempotency-Key ausente o vacía
IDEM-->>API: 400 Bad Request
Idempotency-Key header is required
API-->>GW: 400
GW-->>Client: 400 Bad Request
else Idempotency-Key presente
IDEM->>IDEM: Generate SHA-256 requestHash(payload)
IDEM->>MONGO: findById(idempotencyKey)
alt No existe registro
MONGO-->>IDEM: empty
IDEM->>MONGO: insert PROCESSING
_id = idempotencyKey
requestHash
status = PROCESSING
expiresAt
alt Insert exitoso / lock adquirido
MONGO-->>IDEM: PROCESSING saved
IDEM->>NODE: POST /invoke
payload original
alt Dominio responde OK
NODE-->>IDEM: 200 OK
domainResponse
IDEM->>MONGO: update status COMPLETED
responseStatus = 200
responseBody = domainResponse
MONGO-->>IDEM: COMPLETED saved
IDEM-->>API: 200 OK
domainResponse
API-->>GW: 200 OK
GW-->>Client: 200 OK
respuesta original
else Dominio responde error
NODE-->>IDEM: 500 Error
IDEM->>MONGO: update status FAILED
errorMessage
MONGO-->>IDEM: FAILED saved
IDEM-->>API: 500 Internal Server Error
Domain execution failed
API-->>GW: 500
GW-->>Client: 500 Internal Server Error
end
else DuplicateKeyException / otro request ganó el lock
MONGO-->>IDEM: duplicate key
IDEM-->>API: 409 Conflict
Request is already processing
API-->>GW: 409
GW-->>Client: 409 Conflict
end
else Existe registro
MONGO-->>IDEM: existing record
IDEM->>IDEM: compare existing.requestHash
vs current requestHash
alt Misma key pero payload diferente
IDEM-->>API: 422 Unprocessable Entity
Idempotency-Key reused with different payload
API-->>GW: 422
GW-->>Client: 422 Unprocessable Entity
else Payload igual y status COMPLETED
IDEM-->>API: cached response
status + responseBody
API-->>GW: respuesta cacheada
GW-->>Client: misma respuesta original
else Payload igual y status PROCESSING
IDEM-->>API: 409 Conflict
Request is already processing
API-->>GW: 409
GW-->>Client: 409 Conflict
else Payload igual y status FAILED
IDEM-->>API: 500 Internal Server Error
Previous request failed
API-->>GW: 500
GW-->>Client: 500 Internal Server Error
end
end
end
```
---
- Diagrama de Componentes:
```mermaid
flowchart TB
CLIENT[Cliente / Postman]
subgraph LAB[Idempotency Lab Local]
subgraph GATEWAY[Gateway Service
Spring Cloud Gateway :8080]
ROUTES[Route Locator
/api/**]
REWRITE[RewritePath Filter
/api/execute -> /idempotency/execute]
PROXY[Reactive Proxy]
end
subgraph IDEM[Idempotency Service
Spring Boot WebFlux :8081]
CONTROLLER[IdempotencyController
POST /idempotency/execute]
SERVICE[IdempotencyService
Orquestador]
HASHER[Request Hasher
SHA-256 payload]
POLICY[Policy Handler
TTL / estados / errores]
WEBCLIENT[WebClient
HTTP client reactivo]
REPOSITORY[Reactive Mongo Repository]
end
subgraph DOMAIN[Lambda Node Domain
Node.js :3001]
EXPRESS[Express Adapter
POST /invoke]
HANDLER[Lambda Handler
business simulation]
ERROR_SIM[Forced Error Simulator
forceError=true]
end
subgraph DATA[MongoDB :27017]
IDEM_COLLECTION[(idempotency_records)]
DLQ_COLLECTION[(failed_events opcional)]
end
end
CLIENT -->|POST /api/execute
Idempotency-Key + JSON| ROUTES
ROUTES --> REWRITE
REWRITE --> PROXY
PROXY --> CONTROLLER
CONTROLLER --> SERVICE
SERVICE --> HASHER
SERVICE --> POLICY
SERVICE --> REPOSITORY
REPOSITORY -->|findById / save / update| IDEM_COLLECTION
SERVICE -->|solo si request nuevo| WEBCLIENT
WEBCLIENT --> EXPRESS
EXPRESS --> HANDLER
HANDLER --> ERROR_SIM
HANDLER -->|domain response| EXPRESS
EXPRESS --> WEBCLIENT
WEBCLIENT --> SERVICE
SERVICE -->|save COMPLETED / FAILED| REPOSITORY
SERVICE -->|response cacheada o nueva| CONTROLLER
CONTROLLER --> PROXY
PROXY --> CLIENT
POLICY -.errores controlados.-> DLQ_COLLECTION
```
---
### Licencia:
Este proyecto está bajo la Licencia MIT. Consulta el archivo LICENSE para más detalles.
### Autor:
- [Raul R. Bolivar Navas](https://github.com/raulrobinson/idempotency-lab-opensource)