AWS Builder Center

Building a Production-Ready Event-Driven Fraud Detection System on AWS

Learn how to design a scalable real-time fraud detection platform using AWS, Spring Boot, Kafka, Redis, and MySQL. This article explores event-driven architecture, low-latency risk evaluation, asynchronous processing, security, fault tolerance, observability, and production scalability.

Modern financial systems process thousands of transactions every second. A fraud detection system therefore cannot simply query a database, execute a few rules, and return a response.
The system must make decisions with low latency, remain available during traffic spikes, process large volumes of events, and prevent a failure in one component from bringing down the entire transaction pipeline.
In this article, we will design a real-time fraud detection architecture using AWS, Spring Boot, Kafka, Redis, and MySQL, while focusing on the engineering decisions behind the architecture.

1. Problem Statement

Consider a payment transaction:
1
User → Payment System → Fraud Detection → Decision
For every transaction, the fraud detection layer may need to evaluate:
  • User transaction history
  • Blacklisted accounts
  • Transaction velocity
  • Suspicious IP addresses
  • Unusual transaction amounts
  • Geographic anomalies
  • Previously detected fraud
  • Account risk level
A naive implementation might perform multiple database queries for every transaction:
1
2
3
4
5
6
7
8
9
10
11
12
13
Request
↓
Query User
↓
Query Blacklist
↓
Query Transactions
↓
Calculate Risk
↓
Store Result
↓
Response
This approach becomes problematic as transaction volume increases.
If every request requires multiple synchronous database operations, database latency eventually becomes part of the transaction latency.
The solution is to separate the critical transaction path from the asynchronous processing path.

2. High-Level Architecture

A production-oriented architecture can be structured as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
┌──────────────────┐
│ Client │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ API Gateway │
└────────┬─────────┘
│
▼
┌────────────────────────┐
│ Spring Boot Fraud API │
│ on ECS/Fargate │
└───────────┬────────────┘
│
┌──────────────┴──────────────┐
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Redis │ │ MySQL │
│ ElastiCache │ │ RDS │
└─────────────┘ └─────────────┘
│
│
▼
┌─────────────────┐
│ Amazon MSK │
│ Kafka │
└────────┬────────┘
│
┌───────┴────────┐
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Fraud Worker │ │ Audit Worker │
└──────┬───────┘ └──────┬───────┘
│ │
└────────┬─────────┘
▼
┌─────────────────┐
│ Amazon S3 / │
│ CloudWatch │
└─────────────────┘
The important architectural decision here is that not every operation has to be synchronous.

3. Why Event-Driven Architecture?

Kafka provides an event streaming layer between producers and consumers.
Instead of directly calling every downstream service:
1
2
3
API → Fraud Service → Audit Service
→ Notification Service
→ Analytics Service
we can publish an event:
1
2
3
4
5
6
7
8
API
↓
Kafka Topic
↓
├── Fraud Consumer
├── Audit Consumer
├── Analytics Consumer
└── Notification Consumer
This provides several advantages.

Loose Coupling

The transaction API does not need to know how every downstream service works.
A new consumer can be added without modifying the producer.

Independent Scaling

If fraud processing requires 10 consumers while audit processing requires only 2, they can scale independently.

Fault Isolation

If the audit service temporarily fails, the transaction producer does not necessarily have to fail.
Kafka retains the events so consumers can process them later.

Replayability

Events can be reprocessed when necessary.
This is particularly useful when fraud detection logic changes and historical transactions need to be analyzed again.

4. Designing the Transaction Flow

A transaction request could look like:
1
2
3
4
5
6
7
8
{
"transactionId": "TXN-10001",
"userId": "USER-123",
"amount": 85000,
"currency": "INR",
"merchantId": "MERCHANT-45",
"ipAddress": "192.168.1.10"
}
The Spring Boot API validates the request and performs the minimum amount of synchronous processing required.
The first step is to check low-latency information from Redis.
For example:
1
2
3
4
5
6
7
Transaction
↓
Redis
├── Blacklist?
├── Previous risk score?
├── Transaction velocity?
└── Temporary restrictions?
If the transaction passes the initial checks, an event can be published to Kafka.
Example event:
1
2
3
4
5
6
7
{
"eventType": "TRANSACTION_CREATED",
"transactionId": "TXN-10001",
"userId": "USER-123",
"amount": 85000,
"timestamp": "2026-10-04T10:30:00Z"
}

5. Why Redis?

One of the biggest bottlenecks in real-time systems is repeatedly querying a relational database for information that changes frequently but can be represented as key-value data.
Redis is useful for:
  • Blacklists
  • Rate limits
  • Transaction counters
  • Temporary risk scores
  • Session information
  • Frequently accessed user risk profiles
For example:
1
2
3
4
5
fraud:blacklist:user:123 → true

fraud:velocity:user:123 → 7

fraud:risk:user:123 → 82
We can also use TTLs.
For example:
1
2
transaction_count:user:123
TTL = 60 seconds
This allows the system to detect rules such as:
More than 10 transactions from the same account within 60 seconds.
Without Redis, the application might repeatedly execute:
1
2
3
4
SELECT COUNT(*)
FROM transactions
WHERE user_id = ?
AND transaction_time >= NOW() - INTERVAL 60 SECOND;
At very high transaction volumes, this can create unnecessary database load.
Redis allows these frequently accessed counters to be maintained in memory.

6. Kafka Partitioning Strategy

Kafka performance depends heavily on partitioning.
Suppose we have:
1
transactions
with 12 partitions.
We can use:
1
userId
as the partition key.
For example:
1
2
3
4
user-101 → Partition 0
user-102 → Partition 4
user-103 → Partition 7
user-101 → Partition 0
This provides an important property:
Events for the same user can be routed to the same partition, preserving their order within that partition.
This can matter for fraud detection because transaction order can affect risk calculations.
However, partition keys must be selected carefully.
If one key receives a disproportionate amount of traffic, it can create a hot partition.
Therefore, partitioning is not simply a configuration detail. It is a scalability decision.

7. Consumer Groups

Kafka consumers can be organized into consumer groups.
For example:
1
2
3
4
5
6
Fraud Consumer Group

Consumer 1 → P0 P1 P2
Consumer 2 → P3 P4 P5
Consumer 3 → P6 P7 P8
Consumer 4 → P9 P10 P11
If transaction traffic increases, more consumers can be added.
This allows the processing layer to scale horizontally.
However, adding consumers beyond the number of partitions does not provide additional parallelism for that topic.
Therefore:
1
Maximum parallel consumers ≈ Number of partitions
This is an important capacity-planning consideration.

8. MySQL as the System of Record

Redis is optimized for fast access, but it should not replace the relational database as the primary system of record.
MySQL can store:
1
2
3
4
5
6
users
transactions
fraud_decisions
blacklist_entries
risk_profiles
audit_records
For example:
1
2
3
4
5
6
7
CREATE TABLE transactions (
transaction_id VARCHAR(50) PRIMARY KEY,
user_id VARCHAR(50) NOT NULL,
amount DECIMAL(15,2) NOT NULL,
status VARCHAR(20),
created_at TIMESTAMP
);
The database provides durable storage and transactional guarantees.
A useful architectural separation is:
1
2
3
4
5
6
7
Redis → Fast temporary state

MySQL → Durable business data

Kafka → Event stream

S3 → Long-term object storage
Each technology is therefore used for the problem it is best suited to solve.

9. Running Spring Boot on AWS

The Spring Boot application can be packaged as a Docker container:
1
2
3
4
5
6
7
8
9
Spring Boot Application
↓
Docker
↓
Amazon ECR
↓
Amazon ECS
↓
AWS Fargate
Amazon ECR stores the container image.
Amazon ECS manages the containers.
AWS Fargate allows the application to run without manually managing EC2 servers.
Multiple ECS tasks can run behind a load balancer:
1
2
3
4
5
Application Load Balancer
/ | \
/ | \
ECS ECS ECS
Task 1 Task 2 Task 3
If traffic increases, ECS can increase the number of running tasks.

10. Security Architecture

A financial application cannot treat security as an afterthought.
The architecture should use multiple layers of protection.

IAM

Each AWS component should receive only the permissions it requires.
For example:
1
2
3
4
5
Fraud Service
↓
Read specific secrets
Publish to specific Kafka resources
Read/write specific database resources
Avoid giving an application broad administrative permissions.
This follows the principle of least privilege.

VPC

Application services should be placed inside a controlled VPC.
A typical design is:
1
2
3
4
5
6
7
8
9
10
Internet
↓
Load Balancer
↓
Public Subnet
↓
Private Subnet
├── ECS
├── Redis
└── Database
The database should not be directly accessible from the public internet.

Secrets

Database passwords, API credentials, and other sensitive values should not be hardcoded into:
1
application.properties
They should be managed using services such as:
AWS Secrets Manager

11. Idempotency

Distributed systems can deliver the same event more than once.
Suppose:
1
TXN-10001
is processed successfully, but the consumer crashes before acknowledging the Kafka message.
The message may be processed again.
Without idempotency:
1
2
3
4
5
TXN-10001
↓
Fraud Decision
↓
Fraud Decision AGAIN
The application should therefore maintain an idempotency mechanism.
For example:
1
2
transaction_id = TXN-10001
processing_status = COMPLETED
Before processing an event:
1
2
3
4
Does transaction already have a completed decision?

YES → Ignore duplicate
NO → Process transaction
This is an essential concept when designing distributed systems.

12. Handling Failures

Production systems should assume that components will fail.
Possible failures include:
1
2
3
4
5
6
Redis unavailable
Kafka consumer crash
Database connection failure
Network timeout
Application crash
AWS service throttling
The architecture should therefore implement:
  • Retry mechanisms
  • Dead-letter topics
  • Timeouts
  • Circuit breakers
  • Idempotent processing
  • Health checks
  • Monitoring
  • Graceful degradation
For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
Kafka
↓
Consumer
↓
Processing Failed
↓
Retry
↓
Retry
↓
Retry
↓
Dead Letter Topic
A dead-letter topic prevents permanently failing messages from blocking normal processing.

13. Observability

A distributed architecture without observability becomes extremely difficult to debug.
CloudWatch can be used to monitor:
1
2
3
4
5
6
7
8
API latency
CPU utilization
Memory utilization
Error rate
Kafka consumer lag
Database connections
Redis latency
Request throughput
For example, an increase in Kafka consumer lag could indicate:
1
2
3
4
5
6
7
Incoming events
↓
1000 events/sec

Consumers
↓
600 events/sec
The backlog will continuously increase.
This is a signal that the consumer layer needs additional capacity or optimization.
Monitoring should therefore focus not only on infrastructure metrics but also on business and system-level metrics.

14. Scaling Strategy

Suppose the system initially handles:
1
1,000 transactions/sec
and eventually grows to:
1
10,000 transactions/sec
Scaling cannot simply mean increasing the CPU of one server.
Instead, the architecture should scale horizontally:
1
2
3
4
5
6
7
8
9
10
11
Load Balancer
/ | \
ECS ECS ECS
↓ ↓ ↓
Kafka
/ | \
C1 C2 C3
\ | /
Redis
↓
RDS
Each layer can scale independently.
This is one of the major benefits of a decoupled architecture.

15. The Critical Path vs Asynchronous Path

One of the most important architectural decisions is determining what must happen before the transaction response and what can happen later.

Critical path

1
2
3
4
5
6
7
8
9
10
11
Request
↓
Authentication
↓
Basic validation
↓
Redis risk checks
↓
Fraud decision
↓
Response

Asynchronous path

1
2
3
4
5
6
7
Transaction Event
↓
Kafka
↓
┌─────┼────────┐
↓ ↓ ↓
Audit Analytics Notifications
This separation prevents non-critical workloads from unnecessarily increasing transaction latency.

16. Why Not Use Only a Database?

A common question is:
Why not simply use MySQL for everything?
MySQL is excellent for durable relational data, but using it for every real-time operation creates unnecessary pressure on the database.
For example, if every transaction performs:
1
2
3
5 database reads
+
2 database writes
then:
1
2
3
4
5
10,000 transactions/sec
×
7 database operations
=
70,000 database operations/sec
This can quickly become a bottleneck.
Instead:
1
2
3
4
5
6
7
8
9
10
11
Redis
→ frequently accessed state

Kafka
→ asynchronous event processing

MySQL
→ durable relational data

S3
→ large-scale object storage
This division allows each component to perform the workload it is designed for.

17. Cost Considerations

Scalability should not mean unlimited infrastructure.
AWS architecture should be designed around actual workload requirements.
Potential optimization strategies include:
  • Right-sizing ECS tasks
  • Configuring appropriate Kafka partitions
  • Setting Redis memory policies
  • Using S3 lifecycle policies
  • Controlling CloudWatch log retention
  • Using autoscaling
  • Avoiding unnecessary cross-region traffic
  • Monitoring database utilization
The objective is not simply to build the most powerful system.
The objective is to build a system that provides the required latency, reliability, scalability, and security at a reasonable cost.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article