[ System Architecture ]
Event-Driven vs. Message-Based: The Misunderstanding That Breaks Systems
Many systems use asynchronous communication, but not every async system is event-driven. Confusing the two leads to tight coupling, duplicate processing bugs, and systems that are harder to evolve than the monoliths they replaced.
Janith Ranasinghe
Associate Software Engineer @ FACTn Software | R&D | DevOps
6 min read

Many systems today use asynchronous communication, but not every async system is event-driven. This misunderstanding leads to tightly coupled architectures, duplicate processing bugs, and systems that are harder to evolve than the monoliths they replaced.
Let’s break this down properly.
The root of the confusion
At a glance, both approaches look similar:
- Messages are sent
- Consumers receive them
- Work happens asynchronously
So teams assume: “Event-driven = messaging system.” But that is where things go wrong.
Event-Driven Architecture (EDA)
An event is a fact:
Something already happened.
Examples:
- UserRegistered
- OrderPlaced
- PaymentCompleted
Key characteristics
- Events represent state changes
- Producers do not care who consumes them
- Messages are immutable
- Multiple consumers can react independently
- Events can be replayed
Think of it as broadcasting history.
Message-Based Communication
A message is usually a command:
Please do this.
Examples:
- SendEmail
- GenerateInvoice
- ProcessPayment
Key characteristics
- Often point-to-point
- The consumer is expected to handle the task
- The message is removed after consumption
- Typically used with queues
Think of it as delegating work.
The core difference

Real-world example: a notification service
I built a Notification Service using Kafka for event streaming, Redis for idempotency, PostgreSQL for storage, and Firebase / Azure Communication Services for delivery.
Event-driven flow
User Service → publishes → UserRegistered event
↓
Kafka Topic (user-events)
↓
Notification Service consumes
↓
Sends Email / Push NotificationHere:
- The producer does not know about notifications
- Multiple services can consume the same event
- Events can be replayed if needed
This is true event-driven architecture.
Message-based flow (common mistake)
User Service → sends → SendWelcomeEmail message
↓
Queue
↓
Notification Service consumes
↓
Sends EmailHere:
- The producer knows exactly what should happen
- This is not event-driven
- This is task delegation
This is messaging — not EDA.
The dangerous misuse: using Kafka like a queue
This happens when teams publish commands instead of events, have only one consumer group, delete or ignore history, and treat topics like work queues.
Kafka Topic: send-email
Message: SendEmail(userId)This is just a queue with extra complexity. You lose:
- Replay capability
- Decoupling
- Flexibility
When to use each
Use event-driven when
- Multiple systems should react independently
- You need audit logs or history
- You want loose coupling
- You expect future consumers
Typical use cases include auditing, data synchronization, analytics pipelines, and notifications triggered by events.
Use message-based when
- You need guaranteed processing of a task
- There is one clear consumer
- You want simple task execution
Typical use cases include sending emails as a direct task, image processing, payment execution, and background jobs.
Idempotency: the hidden requirement
In event-driven systems, duplicate processing will happen because of at-least-once delivery, retries, and consumer failures.
Use idempotency.
One approach is to store processed event IDs in Redis and ignore duplicates:
if (redis.exists(eventId)) return;
process();
redis.set(eventId);Without this, duplicate emails, double charges, and inconsistent data become real risks.
Key insight
Event-driven is about facts. Messaging is about actions.
If your system is saying “Do this,” it is messaging. If it is saying “This happened,” it is event-driven.
