Este proyecto es una PoC de Payment Processing. Implementa una arquitectura basada en Domain-Driven Design, Arquitectura Hexagonal, CQRS, Event Sourcing y un modelo de actores inspirado en DomoActors para procesar comandos de forma secuencial por agregado.
El microservicio gestiona el ciclo de vida de un pago:
- Iniciar un pago.
- Autorizarlo.
- Capturarlo.
- Fallarlo.
- Cancelarlo.
- Reembolsarlo.
- Consultar su estado reconstruido desde eventos.
- Consultar el historial de eventos.
El agregado raíz es Payment.
Estados posibles:
NEWAUTHORIZEDCAPTUREDFAILEDREFUNDEDCANCELLED
InitiatePaymentCommandAuthorizePaymentCommandCapturePaymentCommandFailPaymentCommandRefundPaymentCommandCancelPaymentCommand
PaymentInitiatedPaymentAuthorizedPaymentCapturedPaymentFailedPaymentRefundedPaymentCancelled
Cada cambio de estado se guarda como evento en KurrentDB. El estado actual del pago no se persiste como una fila tradicional, sino que se reconstruye aplicando su historial de eventos.
src/main/java/com/example/payments
├── domain
│ ├── model
│ ├── commands
│ ├── events
│ └── ports
├── application
│ ├── actors
│ └── service
├── infrastructure
│ └── kurrentdb
├── adapters
│ └── rest
└── config
REST Controller
↓
Application Service
↓
Payment Actor
↓
Payment Aggregate
↓
Domain Events
↓
KurrentDB Event Store
El proyecto incluye un PaymentActorSystem y un PaymentActor por paymentId. La finalidad es procesar los comandos de un mismo pago de forma secuencial, evitando condiciones de carrera sobre el agregado.
Este enfoque está inspirado en DomoActors, pero adaptado a Java mediante un actor simple basado en ConcurrentHashMap y ReentrantLock.
cd infraestructura
docker compose up -dKurrentDB quedará disponible en:
http://localhost:2113
mvn spring-boot:runEl servicio queda disponible en:
http://localhost:8080
Swagger UI:
http://localhost:8080/swagger-ui.html
POST /payments/v1/paymentsRequest:
{
"merchantId": "merchant-001",
"customerId": "customer-9001",
"amount": 150.75,
"currency": "PEN",
"paymentMethod": "CARD",
"orderId": "order-2026-0001"
}POST /payments/v1/payments/{paymentId}/authorizations{
"authorizationCode": "AUTH-123456"
}POST /payments/v1/payments/{paymentId}/captures{
"captureReference": "CAP-987654"
}POST /payments/v1/payments/{paymentId}/refunds{
"amount": 50.00,
"reason": "Customer partial refund request"
}POST /payments/v1/payments/{paymentId}/cancellations{
"reason": "Customer cancelled order before capture"
}GET /payments/v1/payments/{paymentId}GET /payments/v1/payments/{paymentId}/events- Un pago inicia en estado
NEW. - Solo un pago
NEWpuede ser autorizado. - Solo un pago
AUTHORIZEDpuede ser capturado. - Solo un pago
CAPTUREDpuede ser reembolsado. - Un pago
NEWoAUTHORIZEDpuede ser cancelado. - Un pago
CAPTUREDoREFUNDEDno puede fallarse.
Archivo:
src/main/resources/application.yml
server:
port: 8080
spring:
application:
name: payment-processing-kurrentdb-java25
kurrentdb:
connection-string: kurrentdb://localhost:2113?tls=falseLevanta KurrentDB:
cd infraestructura
docker compose up -d
Verifica que esté arriba:
docker ps
KurrentDB queda en: http://localhost:2113
Luego levanta el microservicio:
cd ..
mvn spring-boot:run
El API queda en:
Swagger: http://localhost:8080/swagger-ui.html
Docker files incluidos:
infraestructura/
├── docker-compose.yml # levanta KurrentDB
├── Dockerfile # construye la imagen del microservicio Java
├── requests/ # ejemplos de requests
└── responses/ # ejemplos de responses
Para probar:
curl -X POST http://localhost:8080/payments/v1/payments \
-H "Content-Type: application/json" \
-d @infraestructura/requests/01-initiate-payment.json