Event-Driven Architecture: Uncle Explains Like You're Five 👦👨🦳
Source: Dev.to
A conversation between Uncle (a backend architect) and Nephew (a curious developer) about events, publishers, subscribers, Redis Pub/Sub, RabbitMQ, and Kafka
The Beginning: What is an Event?
👦 Nephew: Uncle, I see words like “Redis Pub/Sub”, “RabbitMQ”, “Kafka” everywhere in job descriptions. What do they all do? Are they the same thing?
👨🦳 Uncle: (smiles) No, they’re different. But before I confuse you with names, let me ask you something. Have you ever watched the news on TV?
👦 Nephew: Yes uncle, every morning!
👨🦳 Uncle: Perfect! So when the news channel says “Breaking News: India won the cricket match” - what happened?
👦 Nephew: Something important occurred… and they announced it!
👨🦳 Uncle: Exactly! That “something important occurred” is called an Event. In software, when something happens - like a user placing an order, a payment succeeding, or a file uploading - that’s an event.
👦 Nephew: So event = something that happened?
👨🦳 Uncle: Yes! Think of it as news. When you place an order at Zomato, that’s an event. When you get a payment notification from Google Pay, that’s an event. When someone follows you on Instagram, that’s an event.
👦 Nephew: Okay, I get it. But uncle, why is this “event” concept important? I can just write code directly, right?
👨🦳 Uncle: Ah! That’s where the real story begins…
The Problem: Spaghetti Code
👨🦳 Uncle: Imagine you own a food delivery company. A customer places an order. Now, what all needs to happen?
👦 Nephew: Umm… save the order, send email confirmation, alert the restaurant?
👨🦳 Uncle: Good! Let me write the code without events:
function placeOrder(orderId) {
saveOrder(orderId);
sendEmailConfirmation(orderId);
sendSMSNotification(orderId);
notifyRestaurant(orderId);
updateAnalyticsDashboard(orderId);
addLoyaltyPoints(orderId);
updateInventory(orderId);
}
Enter fullscreen mode
Exit fullscreen mode
Now, one day your boss says “Also send WhatsApp notification”. What do you do?
👦 Nephew: Add another function call in the same code?
👨🦳 Uncle: Yes, but now the function becomes bigger. Tomorrow boss says “Also notify admin”, you add more. Your Order Service becomes this giant blob of code that does 10 different things. It becomes:
Hard to maintain - Change one thing, something else breaks
Tightly coupled - Every service knows about every other service
Hard to scale - One service dying affects everything
This is called spaghetti code. Everyone is tangled together.
👦 Nephew: That sounds awful! So what’s the solution?
👨🦳 Uncle: What if the Order Service just announces “Order Created” and doesn’t care who’s listening?
Enter: Publisher & Subscriber
👨🦳 Uncle: Remember the news channel? The channel broadcasts news. The viewers listen if they want. The channel doesn’t know who’s watching - could be 1 person or 1 million.
👦 Nephew: Yes, like YouTube!
👨🦳 Uncle: Perfect! YouTube is the Publisher - they create content. You are the Subscriber - you listen. The video upload is the Event.
Now in our food delivery system:
Order Service = Publisher (creates the event “Order Created”)
Email Service, SMS Service, Analytics = Subscribers (listen to that event)
The Order Service just announces:
📢 "Order Created - Order #123"
Enter fullscreen mode
Exit fullscreen mode
And doesn’t care who listens!
👦 Nephew: So if tomorrow boss wants WhatsApp notification, I just add another subscriber?
👨🦳 Uncle: Exactly! Order Service doesn’t change. You just create a WhatsApp Service that subscribes to “Order Created” events. Beautiful decoupling!
👦 Nephew: Amazing! But uncle, how do they communicate? There must be something in the middle passing messages around?
👨🦳 Uncle: Yes! That’s where Redis Pub/Sub comes in…
Redis Pub/Sub: The Simple Messenger
👨🦳 Uncle: Think of it like a WhatsApp group. You have a group called “Order Events”. When someone posts a message, everyone in the group sees it.
👦 Nephew: Oh! So Order Service is like one person posting in the group, and others are reading?
👨🦳 Uncle: Exactly! Here’s how it works:
Order Service (Publisher)
|
| Publishes to channel: "order-created"
v
Redis
|
-----+-----
| | |
v v v
Email SMS Analytics
Service Service Service
(Subscribers)
Enter fullscreen mode
Exit fullscreen mode
Each service subscribes to a “channel” - like joining a WhatsApp group.
Order Service publishes:
redis.publish('order-created', {
orderId: 123,
userId: 456,
amount: 500
});
Enter fullscreen mode
Exit fullscreen mode
Email Service listens:
redis.subscribe('order-created', (message) => {
sendEmail(message.userId);
});
Enter fullscreen mode
Exit fullscreen mode
👦 Nephew: So Redis is like the WhatsApp server?
👨🦳 Uncle: Exactly! Redis is the messenger sitting in the middle, broadcasting messages.
👦 Nephew: This seems perfect! Why would we need anything else?
👨🦳 Uncle: Ah, great question! Here comes the problem…
The Crash: Redis Pub/Sub’s Dirty Secret
👨🦳 Uncle: Imagine it’s 10:00 AM. Order Service publishes:
"Order Created - Order #123"
Enter fullscreen mode
Exit fullscreen mode
But at that exact moment, Email Service has crashed (network issue). Redis broadcasts the message. Email Service isn’t listening.
Message is gone. Forever.
👦 Nephew: Oh no! So the customer never gets the confirmation email?
👨🦳 Uncle: Exactly! At 10:01 AM, Email Service comes back online. Can it ask Redis “Hey, what messages did I miss?”
👦 Nephew: Can’t it?
👨🦳 Uncle: No! Redis says “Sorry, that message was 1 minute ago. I already sent it. I don’t save messages.”
Think of it like FM Radio:
-
Radio broadcasts live music
-
If you’re listening: ✅ You hear it
-
If you’re not listening: ❌ You miss it
-
Radio doesn’t replay old songs
👦 Nephew: That’s a problem for important stuff like payment notifications!
👨🦳 Uncle: Exactly! What if the event is “Payment Success”? We CANNOT lose that message. A customer should get confirmation even if they restart their phone.
For such critical events, we need something that:
Stores messages (doesn’t lose them)
Retries (if service is down, deliver later)
Confirms (acknowledges delivery)
Redis Pub/Sub can’t do any of this. And this pain gave birth to the next hero…
RabbitMQ: The Reliable Messenger
👦 Nephew: What can RabbitMQ do that Redis can’t?
👨🦳 Uncle: Let me draw you a scenario. Same 10:00 AM order.
Order Service sends to RabbitMQ:
Order #123 Created
Enter fullscreen mode
Exit fullscreen mode
RabbitMQ says “Got it! I’m storing this message in a Queue.”
Now, even if Email Service is down, the message stays in the queue.
When Email Service comes back online at 10:01 AM, RabbitMQ says:
“Hey! You have 1 message. Here it is.”
Email Service reads and processes it.
👦 Nephew: So RabbitMQ is like a post office?
👨🦳 Uncle: Perfect analogy! The post office stores your letters even if the recipient isn’t home. When they come back, they get the letter.
But there’s a twist…
Queues & Workers
👨🦳 Uncle: In RabbitMQ, we don’t have “subscribers listening”. Instead, we have workers pulling messages.
Imagine a pizza shop:
- Kitchen has a Queue of pizza orders written on paper
Worker 1 pulls an order: “Margherita Pizza”
-
Worker 1 makes it and says “ACK” (acknowledgement) - “I got it!”
-
Order is removed from queue
Worker 2 pulls next order: “Pepperoni Pizza”
This ensures:
-
No order is lost
-
No order is made twice
-
If Worker 1 crashes mid-making, the order goes back to queue for another worker
👦 Nephew: What does ACK mean?
👨🦳 Uncle: ACK = “Acknowledgement”. It means “I processed this message, you can delete it from queue now.”
Example:
// Email Service pulling messages
consumer.consume('order-created', async (message) => {
const orderId = message.content;
try {
await sendEmail(orderId); // Process
consumer.ack(message); // ✅ Success, delete from queue
} catch (error) {
consumer.nack(message); // ❌ Failed, put back in queue
// Another worker will retry
}
});
Enter fullscreen mode
Exit fullscreen mode
👦 Nephew: Oh! So if Email Service crashes before sending email, the message goes back to queue?
👨🦳 Uncle: Yes! Another Email Service instance picks it up. The message isn’t lost.
👦 Nephew: So RabbitMQ is perfect now?
👨🦳 Uncle: Not quite… It has its own limitations…
RabbitMQ’s Limitation: Queue is Consumed
👨🦳 Uncle: Here’s the thing. In RabbitMQ, when a message is consumed and ACK’d, it’s deleted forever.
Imagine you have:
-
Email Service
-
SMS Service
-
Analytics Service
All processing the same event “Order Created”.
Email Service: ✅ Processed, ACK’d
SMS Service: ✅ Processed, ACK’d
Analytics Service: Hmm, I crashed. I never got to process it!
In RabbitMQ, the message is gone. Analytics loses the data.
👦 Nephew: So we can’t have multiple independent consumers reading the same message?
👨🦳 Uncle: Not really. Each message is meant for ONE consumer.
RabbitMQ is good for:
-
Tasks: “Send this email”, “Generate this PDF”, “Resize this image”
-
Once done, forget about it
But it’s bad for:
-
Events/Facts: “User Purchased”, “Order Placed”
-
Where history matters
-
Where multiple services need to analyze
👦 Nephew: So what do we do?
👨🦳 Uncle: Here comes the king… Kafka.
Kafka: The Event Store
👨🦳 Uncle: Imagine instead of a trash can (like RabbitMQ), events are stored in a notebook that never gets thrown away.
Topic: orders (a notebook)
Order 101 ← Event 1 (stored forever)
Order 102 ← Event 2 (stored forever)
Order 103 ← Event 3 (stored forever)
Order 104 ← Event 4 (stored forever)
Enter fullscreen mode
Exit fullscreen mode
Now:
-
Email Service reads page 1, 2, 3
-
SMS Service reads page 1, 2, 3 (same pages!)
-
Analytics reads page 1, 2, 3 (same pages!)
The notebook never gets destroyed. Everyone can read whenever they want!
👦 Nephew: Can they reread the same page again?
👨🦳 Uncle: Yes! That’s the magic of Kafka. Let me show you…
The Magic of Offsets & Replay
👨🦳 Uncle: Kafka numbers every event:
Offset 0 → Order 101
Offset 1 → Order 102
Offset 2 → Order 103
Offset 3 → Order 104
Enter fullscreen mode
Exit fullscreen mode
Analytics Service reads Offsets 0, 1, 2. Then it crashes.
It remembers: “I’ve read up to Offset 2.”
When it comes back online, Kafka says: “Where did you leave off?”
Analytics: “Offset 2”
Kafka: “Here’s Offset 3, 4, 5…” (only new ones)
No duplicates! No missed messages!
👦 Nephew: And the notebook never got destroyed?
👨🦳 Uncle: Never! It stays. This enables something magical called Replay.
New team arrives: “Recommendation Team”. They need to analyze last 30 days of purchases.
In RabbitMQ: “Sorry, we already processed and deleted those messages.”
In Kafka: “No problem! Start reading from Offset 0.”
The Recommendation Team reads all 30 days of historical data. Done!
👦 Nephew: That’s amazing! So Kafka is always better than RabbitMQ?
👨🦳 Uncle: It’s different, not better. RabbitMQ is simpler if you just need task queues. Kafka is complex but powerful if you need event history.
But wait, there’s more to Kafka…
The Scaling Problem: Partitions
👦 Nephew: Uncle, what if Amazon generates 10 million events per second? Can one notebook handle it?
👨🦳 Uncle: No! That’s why Kafka invented Partitions.
Instead of:
1 notebook (one notebook is slow)
Enter fullscreen mode
Exit fullscreen mode
It creates:
10 notebooks (10 notebooks in parallel)
Enter fullscreen mode
Exit fullscreen mode
Example:
Topic: orders
Partition 0 Partition 1 Partition 2
------ ------ ------
Order 101 Order 102 Order 103
Order 105 Order 106 Order 104
Order 108 Order 109 Order 107
Enter fullscreen mode
Exit fullscreen mode
Events are distributed across partitions. Each partition can be on a different machine.
Partition 0 → Machine A (writing 3M events/sec)
Partition 1 → Machine B (writing 3M events/sec)
Partition 2 → Machine C (writing 3M events/sec)
Total: 9M events/sec (in parallel!)
Enter fullscreen mode
Exit fullscreen mode
👦 Nephew: So how does Kafka decide which event goes to which partition?
👨🦳 Uncle: It uses a Key. Example:
producer.send({
topic: 'orders',
key: userId, // All events for same user go to same partition
value: { orderId: 123 }
});
Enter fullscreen mode
Exit fullscreen mode
All events for User 101 go to the same partition. This ensures ordering.
👦 Nephew: Ordering? Why does that matter?
👨🦳 Uncle: Imagine User 101:
-
Logs in
-
Purchases iPhone
-
Logs out
If these go to different partitions, they might be processed out of order:
-
“Logs out” (done first)
-
“Logs in” (second)
-
“Purchases” (last)
That’s wrong! The user hasn’t even logged in yet when they purchase!
By sending all User 101 events to the same partition, they stay ordered:
Partition 2
|
v
Login (User 101)
Purchase (User 101)
Logout (User 101)
Always in order!
Enter fullscreen mode
Exit fullscreen mode
👦 Nephew: Brilliant! And what about multiple Email Services reading the same partition?
👨🦳 Uncle: Ah! Here’s where Consumer Groups come in…
Consumer Groups: The Team
👨🦳 Uncle: Kafka says: “One partition can be read by only ONE consumer at a time, but multiple consumers can form a Consumer Group.”
Think team of workers:
Topic: orders (3 partitions)
Consumer Group: Email Service
- Worker A
- Worker B
- Worker C
Kafka assigns:
- Worker A reads Partition 0
- Worker B reads Partition 1
- Worker C reads Partition 2
Enter fullscreen mode
Exit fullscreen mode
Now:
-
No duplicate processing (each partition owned by one worker)
-
Parallel processing (3 workers working simultaneously)
-
Load balanced (if one worker dies, another takes over)
👦 Nephew: What if Worker B crashes?
👨🦳 Uncle: Kafka detects the crash and rebalances:
Before:
- Worker A → Partition 0
- Worker B → Partition 1 (💥 CRASHED)
- Worker C → Partition 2
After:
- Worker A → Partition 0, 1 (rebalanced)
- Worker C → Partition 2
Enter fullscreen mode
Exit fullscreen mode
Processing continues!
Redis vs RabbitMQ vs Kafka: The Comparison
👦 Nephew: Uncle, now I’m confused. When should I use what?
👨🦳 Uncle: Let me create a mental model for each:
Redis Pub/Sub
👨🦳 Uncle: Think FM Radio.
Radio Station broadcasts → Listeners hear (if tuned in)
→ If not listening, message lost
Enter fullscreen mode
Exit fullscreen mode
Good for:
-
Live notifications (“User is typing…”)
-
Live updates (“Stock price changed”)
-
Realtime dashboards
Bad for:
-
Reliable message delivery
-
Message history
RabbitMQ
👨🦳 Uncle: Think Delivery Service.
Sender → Package → Delivery Boy → Receiver
(stored temporarily in warehouse)
Once delivered and signed, package is gone.
Enter fullscreen mode
Exit fullscreen mode
Good for:
-
Task queues (“Send Email”, “Generate PDF”)
-
Work distribution
-
Simple message processing
Bad for:
-
Event history
-
Multiple independent consumers
-
Replay capability
Kafka
👨🦳 Uncle: Think Library Book.
Author writes → Book stored → Many readers can read
(stays on shelf forever)
Anyone can read anytime. History preserved.
Enter fullscreen mode
Exit fullscreen mode
Good for:
-
Event history (“User purchased”, “Payment completed”)
-
Multiple independent services analyzing same events
-
Replay and timeline analysis
-
High-throughput event streaming
Bad for:
-
Simple task queues (too complex)
-
Fire-and-forget scenarios
👦 Nephew: Perfect! So they’re for different use cases?
👨🦳 Uncle: Exactly! Choose based on your need:
Real-time, lose-if-missed? → Redis
Reliable task queue? → RabbitMQ
Event history & multiple consumers? → Kafka
Enter fullscreen mode
Exit fullscreen mode
Real World Example: E-Commerce Order
👦 Nephew: Can you give me a complete real-world example?
👨🦳 Uncle: Sure! Amazon customer purchases iPhone.
Event: “UserPurchased”
Using Kafka, multiple services independently process this:
Order Service publishes:
Topic: user-events
Partition: 0 (because all User #123 events go here)
Event: { userId: 123, productId: iPhone, amount: 50000 }
↓
Kafka stores this forever
↓
Now multiple services can read:
Email Service:
Offset: 0 → Send confirmation email
Analytics Service:
Offset: 0 → Update dashboard, revenue metrics
Fraud Service:
Offset: 0 → Check suspicious purchase pattern
Recommendation Service:
Offset: 0 → Suggest related products
Inventory Service:
Offset: 0 → Update stock count
Loyalty Service:
Offset: 0 → Add reward points
Enter fullscreen mode
Exit fullscreen mode
All reading the same event independently. All storing their own offsets.
If Analytics crashes and comes back after 2 hours:
-
It reads from its last offset
-
Gets all events from the last 2 hours
-
Processes them
The event still exists on Kafka’s Topic, unchanged.
👦 Nephew: This is like a perfect audit trail!
👨🦳 Uncle: Exactly! Netflix keeps Kafka events for 30 days. Any new team can analyze historical data. That’s the power!
Key Takeaways: What to Remember
👦 Nephew: Uncle, so I should just remember…?
👨🦳 Uncle: Remember these mental models:
Event = Something happened (like news)
Publisher = One who creates events (like news channel)
Subscriber = One who listens to events (like viewer)
Redis Pub/Sub = FM Radio (instant, no storage)
RabbitMQ = Delivery Service (reliable, consumed/deleted)
Kafka = Library Books (permanent, many can read)
Partition = Multiple books handling millions of events in parallel
Consumer Group = Team of workers reading partitions
Offset = Page number, remember where you left off
Replay = Read from beginning again (only possible in Kafka)
👦 Nephew: This makes so much sense now!
👨🦳 Uncle: That’s the goal! Once you understand the “why” of events, the tools become simple. They all solve the same problem (decoupling services) with different tradeoffs.
When to Use What: A Decision Tree
👦 Nephew: Any quick tips for choosing?
👨🦳 Uncle: Sure!
Do you need message history?
├─ YES → Use Kafka
├─ NO → Are multiple independent services reading the same message?
├─ YES → Use Kafka (trust me)
├─ NO → Do you need reliable delivery?
├─ YES → Use RabbitMQ
├─ NO → Use Redis Pub/Sub
Enter fullscreen mode
Exit fullscreen mode
Quick examples:
-
Chat app (new message notification) → Redis Pub/Sub
-
Job queue (send 1000 emails) → RabbitMQ
-
Order processing (email, analytics, fraud, inventory) → Kafka
-
Live dashboard updates → Redis Pub/Sub
-
Banking (payment settlement) → RabbitMQ or Kafka
-
User analytics pipeline → Kafka
Final Word
👨🦳 Uncle: Remember, it all starts with understanding the problem. None of these tools matter if you don’t know:
-
What events matter to your business?
-
Who needs to know about them?
-
Can we afford to lose a message?
-
Do we need history?
-
How many events per second?
Once you answer these, the choice becomes obvious.
👦 Nephew: Thank you uncle! This was super clear!
👨🦳 Uncle: You’re welcome! Now go build something with events! Start simple with Redis Pub/Sub, then graduate to RabbitMQ, then maybe Kafka when you need it.
👦 Nephew: Will do, uncle! Less noise, more action! 😄
👨🦳 Uncle: That’s my boy! 👏
Happy event-driven coding!