Skip to main content
FACTn
All resources

[ 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

Janith Ranasinghe

Associate Software Engineer @ FACTn Software | R&D | DevOps

6 min read

One architecture signal branching to many services beside a single task traveling to one service

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

Table comparing event-driven and message-based architecture across purpose, coupling, consumers, data, replay, ownership, and example tools
Event-driven systems describe state changes; message-based systems assign work.

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 Notification

Here:

  • 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 Email

Here:

  • 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.

System architectureEvent-drivenKafka

[Let's start a conversation]

Ready to build your engineering team with confidence?

Free 15-minute introductory call. No sales pressure, just a practical conversation about your goals, team structure, and delivery needs.

Schedule a Free 15-Minute Call

No sales pressure. Just a practical discussion about your needs.