5:48 PM
4G
ria
Money
Transfer
Transfer
Money where you need it
v3.3.1.796
Powered by Sikhona Forex
Quick access
Forgot PIN?
Enter your username below to receive your PIN reset code
Powered by Sikhona Forex
Enterprise Event Stream Inspector
Kafka Broker: kafka.internal:9092 | Topic: transfer-requests
Spring Boot API
200 OK / Healthy
Port 8080 (REST)
Kafka Partition
Partition: #0
Replication: 3
Total Dispatched
1
Events Produced
.NET Consumer Lag
0 ms
Consumer Active
ria-event-broker.internal:9092 stdout
[BOOTSTRAP]
Kafka consumer group registered:
'ria-settlement-group'
| Partition assigned: [0, 1, 2]
[READY]
Spring Boot RemittanceController ready on port 8080. Enter PIN and click Send Money in the phone to test.
Distributed Remittance Flow Architecture
This design models real-world enterprise banking at Ria Money Transfer. Ingress is handled by a high-throughput Java Spring Boot API, decoupled from downstream clearing via Apache Kafka, and consumed by a specialized .NET 8 settlement service.
1. Client / Ingress
HTTP/REST
Ria Mobile Client
- Input validation
- 4-digit PIN verification
- Live FX fee calculation
2. Java Spring Boot
Kafka Producer
Domain & FX Engine
- Immutable TransferEvent
- Spring KafkaTemplate
- Idempotency check (UUID)
3. .NET 8 Worker
Consumer
Settlement & Clearing
- BackgroundService
- FICA / AML compliance
- Manual commit after clear
Key Principles Implemented
SOLID Principles:
Single Responsibility across separate Producer / Consumer microservices; Open-Closed for payment gateway routing.
Event-Driven Durability:
Kafka ensures zero transaction loss even if Sikhona partner banking endpoints experience intermittent timeout.
@RestController
@RequestMapping("/api/v1/transfers")
@CrossOrigin(origins = "*")
public class RemittanceController {
private final KafkaTemplate<String, TransferEvent> kafkaTemplate;
private static final String TOPIC = "transfer-requests";
public RemittanceController(KafkaTemplate<String, TransferEvent> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
@PostMapping
public ResponseEntity<TransferResponse> initiateTransfer(@Valid @RequestBody TransferRequest req) {
// Enforce 4-digit PIN verification & domain invariants
if (!"1234".equals(req.pin())) {
throw new BadCredentialsException("Invalid 4-digit security PIN");
}
String txnId = "RIA-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
BigDecimal fxRate = BigDecimal.valueOf(0.054);
BigDecimal payout = req.amount().multiply(fxRate);
TransferEvent event = new TransferEvent(
txnId,
req.username(),
req.recipientName(),
req.amount(),
payout,
req.destinationCurrency(),
Instant.now()
);
// Asynchronously publish to Kafka broker
kafkaTemplate.send(TOPIC, txnId, event);
return ResponseEntity.accepted().body(new TransferResponse(txnId, "QUEUED_FOR_SETTLEMENT"));
}
}
using Confluent.Kafka;
using System.Text.Json;
public class TransferSettlementWorker : BackgroundService {
private readonly ILogger<TransferSettlementWorker> _logger;
private readonly ConsumerConfig _config = new() {
BootstrapServers = "kafka:9092",
GroupId = "ria-settlement-group",
AutoOffsetReset = AutoOffsetReset.Earliest,
EnableAutoCommit = false
};
protected override async Task ExecuteAsync(CancellationToken token) {
using var consumer = new ConsumerBuilder<string, string>(_config).Build();
consumer.Subscribe("transfer-requests");
while (!token.IsCancellationRequested) {
var msg = consumer.Consume(token);
var transfer = JsonSerializer.Deserialize<TransferEvent>(msg.Message.Value);
_logger.LogInformation("Settling transaction {TxnId} for {Amount} ZAR",
transfer.TransactionId, transfer.SendAmount);
// Simulate banking partner clearing
await Task.Delay(1500, token);
consumer.Commit(msg);
}
}
}
version: '3.8'
services:
zookeeper:
image: confluentinc/cp-zookeeper:7.5.0
environment:
ZOOKEEPER_CLIENT_PORT: 2181
kafka:
image: confluentinc/cp-kafka:7.5.0
depends_on: [zookeeper]
ports: ["9092:9092"]
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
spring-boot-api:
build: ./ria-backend-java
ports: ["8080:8080"]
environment:
SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka:9092
dotnet-settler:
build: ./ria-worker-dotnet
environment:
Kafka__BootstrapServers: kafka:9092