Status: Implemented Author: Engineering Team Date: 2025-01-06 Version: 1.0
El sistema de créditos N1 requiere consumir eventos de negocio emitidos por Apache Fineract para:
- Notificar próximos vencimientos de cuotas
- Alertar sobre cuotas vencidas
- Detectar cambios en clasificación de mora (delinquency)
Fineract emite estos eventos mediante su sistema de External Events que serializa mensajes en formato Apache Avro y los publica a un message broker (Kafka o JMS/ActiveMQ).
| Challenge | Descripción |
|---|---|
| Serialización Avro | Fineract usa Avro con schemas propietarios. Solo existe librería Java oficial (fineract-avro-schemas) |
| Consumer .NET | El backend principal (n1-credits-backend) está desarrollado en .NET 8.0 |
| Costo de infraestructura | Kafka (Confluent Cloud) representa ~$150-200 USD/mes para bajo volumen |
| Complejidad operacional | Mantener un cluster Kafka para eventos esporádicos es overkill |
Implementar un Event Bridge usando:
- Amazon MQ (ActiveMQ) como message broker (~$20 USD/mes)
- AWS Lambda (Java 17) para deserializar Avro y transformar a JSON
- Webhook HTTP para entregar eventos a
n1-credits-backend
┌─────────────────────────────────────────────────────────────────────────────┐
│ AWS Cloud │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Fineract │ │ Amazon MQ │ │ Lambda │ │
│ │ (Java 21) │────▶│ (ActiveMQ) │────▶│ (Java 17) │ │
│ │ │ JMS │ │ ESM │ │ │
│ │ │Avro │ Queue: │ │ Deserialize │ │
│ │ │ │ fineract- │ │ Avro → JSON │ │
│ │ │ │ events-jms │ │ │ │
│ └──────────────┘ └──────────────┘ └──────┬───────┘ │
│ │ │
│ │ HTTP POST │
│ │ (JSON) │
│ ▼ │
│ ┌──────────────┐ │
│ │ n1-credits │ │
│ │ -backend │ │
│ │ (.NET 8) │ │
│ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
1. Fineract COB Job ejecuta
└─▶ CheckLoanRepaymentDueBusinessStep
└─▶ CheckLoanRepaymentOverdueBusinessStep
└─▶ SetLoanDelinquencyTagsBusinessStep
2. Business Event emitido
└─▶ Serializado como MessageV1 (Avro)
└─▶ Enviado via JMS a Amazon MQ
3. Lambda triggered por Event Source Mapping
└─▶ Recibe batch de mensajes (Base64)
└─▶ Deserializa MessageV1 wrapper
└─▶ Deserializa data payload dinámicamente
└─▶ Transforma a JSON unificado
4. Webhook enviado a n1-credits-backend
└─▶ HTTP POST con JSON payload
└─▶ Headers: X-Fineract-Event-Type, X-Api-Key
Input (Avro - MessageV1):
┌─────────────────────────────────────┐
│ MessageV1 (Avro) │
├─────────────────────────────────────┤
│ id: long │
│ source: string │
│ type: string (event type) │
│ category: string │
│ createdAt: string │
│ businessDate: string │
│ tenantId: string │
│ idempotencyKey: string │
│ dataschema: string (FQCN) │
│ data: bytes (nested Avro) │
└─────────────────────────────────────┘
Output (JSON):
{
"messageId": "uuid",
"eventType": "LoanRepaymentDueBusinessEvent",
"category": "Loan",
"source": "fineract",
"tenantId": "default",
"createdAt": "2025-01-06T10:30:00",
"businessDate": "2025-01-06",
"idempotencyKey": "unique-key",
"schema": "org.apache.fineract.avro.loan.v1.LoanRepaymentDueDataV1",
"data": {
"loanId": 12345,
"loanAccountNo": "000000012345",
"installment": { ... }
}
}| Property | Value |
|---|---|
| Engine | ActiveMQ |
| Instance | mq.t3.micro |
| Deployment | Single-instance |
| Protocol | OpenWire (SSL) |
| Port | 61617 |
| Cost | ~$20 USD/month |
Justification: Amazon MQ con ActiveMQ soporta el protocolo OpenWire nativo de Fineract sin modificaciones al código fuente.
| Property | Value |
|---|---|
| Runtime | Java 17 (Corretto) |
| Memory | 512 MB |
| Timeout | 60 seconds |
| Trigger | Amazon MQ Event Source Mapping |
| Batch Size | 10 messages |
Dependencies:
fineract-avro-schemas:0.0.1114-a1b6f15- Schemas Avro oficialesaws-lambda-java-events:3.11.4- ActiveMQ event typesjackson-*:2.17.0- JSON serializationokhttp3:4.12.0- HTTP client
Type: MQ
Properties:
Broker: arn:aws:mq:region:account:broker:name:id
Queues: [fineract-events-jms]
SourceAccessConfigurations:
- Type: BASIC_AUTH
URI: !Ref SecretsManagerSecret
BatchSize: 10
MaximumBatchingWindowInSeconds: 5| Criterio | Kafka | Amazon MQ |
|---|---|---|
| Costo mensual | ~$150-200 | ~$20 |
| Modificar Fineract | No | No |
| Complejidad | Alta | Baja |
| Volumen esperado | Overkill | Adecuado |
Decision: Amazon MQ. El volumen de eventos (COB jobs nocturnos) no justifica la complejidad y costo de Kafka.
| Criterio | Lambda | ECS/EC2 |
|---|---|---|
| Costo (bajo volumen) | ~$0 | ~$15-30/mes |
| Escalabilidad | Automática | Manual/Config |
| Operación | Serverless | Managed |
| Cold start | ~3-5s (Java) | N/A |
Decision: Lambda. El patrón de eventos (batch nocturno) tolera cold starts y el costo es prácticamente $0.
| Criterio | Webhook | Azure Service Bus | Direct DB |
|---|---|---|---|
| Acoplamiento | Bajo | Medio | Alto |
| Cross-cloud | Sí | Sí | No |
| Debugging | Fácil | Medio | Difícil |
| Retry logic | En Lambda | En consumer | Manual |
Decision: Webhook HTTP. Permite desacoplamiento total, fácil testing (webhook.site), y el backend .NET solo implementa un endpoint REST.
| Criterio | Java Lambda | .NET Lambda |
|---|---|---|
| Avro deserialization | Nativa (JAR) | Manual (generar schemas) |
| Fineract compatibility | 100% | Requiere conversión |
| Maintenance | Actualizar JAR | Re-generar clases |
Decision: Java. La librería fineract-avro-schemas solo existe en Java. Deserializar Avro en .NET requeriría extraer schemas y generar clases C#, con riesgo de incompatibilidad.
Approach: Modificar Fineract para usar AMQP con Azure Service Bus.
Why Rejected:
- Requiere agregar dependencia Qpid JMS a Fineract
- Modificación del código fuente de Fineract
- Riesgo en upgrades futuros
Approach: Polling directo a tabla m_external_event de Fineract.
Why Deferred:
- Viable como fallback
- Costo $0
- Menor real-time capability
- Acopla consumer a schema de BD
Approach: Usar sistema nativo de Hooks de Fineract.
Why Not Viable:
- Hooks solo funcionan para API commands
- COB Job events no triggean hooks
- Documentado en investigación inicial
| Secret | Storage | Rotation |
|---|---|---|
| MQ credentials | AWS Secrets Manager | Manual |
| Webhook API key | Lambda env var | Manual |
- Amazon MQ: SSL/TLS en puerto 61617
- Lambda → Webhook: HTTPS only
- Security Groups: Restringir acceso a MQ
Los eventos contienen:
- IDs de préstamos (no sensible)
- Montos de cuotas (sensible - PII)
- Fechas de vencimiento (no sensible)
Mitigation: HTTPS en tránsito, no persistencia en Lambda.
- Lambda logs: CloudWatch
/aws/lambda/fineract-mq-consumer-dev - Log format: JSON structured (Log4j2)
- Retention: 14 días
| Metric | Source | Alert Threshold |
|---|---|---|
| Lambda Errors | CloudWatch | > 0 |
| Lambda Duration | CloudWatch | > 30s |
| MQ Queue Depth | Amazon MQ | > 100 |
- Event ID propagado en logs
- Idempotency key para deduplicación
- Correlation via
X-Fineract-Event-Typeheader
# Build
mvn clean package -DskipTests
# Deploy
sam deploy \
--stack-name fineract-mq-lambda-{env} \
--parameter-overrides \
Environment={env} \
MqBrokerArn={arn} \
MqUsername={user} \
MqPassword={pass} \
MqQueueName=fineract-events-jms \
WebhookUrl={url}| Environment | MQ Broker | Webhook Target |
|---|---|---|
| dev | n1co-dev-credits-broker | webhook.site (test) |
| staging | TBD | n1-credits-backend staging |
| prod | TBD | n1-credits-backend prod |
| Component | Cost |
|---|---|
| Amazon MQ mq.t3.micro | $20 |
| Lambda invocations | ~$0 |
| CloudWatch Logs | ~$1 |
| Secrets Manager | ~$0.40 |
| Total | ~$22 USD/month |
| Solution | Monthly Cost |
|---|---|
| Confluent Kafka | $150-200 |
| Self-hosted Kafka | $50-100 |
| Amazon MQ + Lambda | $22 |
| Database Polling | $0 |
- Implementar endpoint webhook en n1-credits-backend
- Configurar alertas CloudWatch
- Documentar runbook operacional
- Agregar Dead Letter Queue (DLQ)
- Implementar retry con exponential backoff
- Dashboard de monitoreo
- Evaluar migración a EventBridge Pipes
- Considerar multi-region para DR
- Schema registry para versionado
- Fineract External Events Documentation
- Amazon MQ Lambda Triggers
- Apache Avro Specification
- Internal: fineract-loan-business-events.md
| Event | Trigger | Use Case |
|---|---|---|
LoanRepaymentDueBusinessEvent |
N días antes de vencimiento | Recordatorio de pago |
LoanRepaymentOverdueBusinessEvent |
N días después de vencimiento | Alerta de atraso |
LoanDelinquencyRangeChangeBusinessEvent |
Cambio de bucket de mora | Gestión de cobranza |
fineract-mq-lambda/
├── pom.xml # Maven config + shade plugin
├── template.yaml # SAM/CloudFormation template
├── README.md # Operational documentation
├── docs/
│ └── DESIGN.md # This document
└── src/main/
├── FineractMqHandler.java # Lambda entry point
├── AvroDeserializer.java # Avro deserialization
├── MessageTransformer.java# JSON transformation
├── SafeObjectWrapper.java # Safe Avro extraction
├── WebhookSender.java # HTTP client
└── resources/
└── log4j2.xml # Logging config