Message Broker vs Message Bus vs Event Bus
Fast recall
Quick revision
Core answer
ApplicationEventPublisher, Guava EventBus, .NET MediatR) — handlers are JVM objects, no network involved, and a crash loses unhandled events. An event bus is usually a synonym for message bus but used in the context of event-driven (rather than command/RPC) communication; the same in-process tools serve both roles. The choice is really 'cross-process broker' vs 'in-process bus' — the broker/bus terminology only adds confusion when used loosely.Whether you can clearly distinguish in-process pub/sub from cross-process messaging infrastructure and pick the right tool for a use case
Treating Spring `ApplicationEventPublisher` as a substitute for Kafka — it's in-process only, has no durability, and crashes lose unhandled events
Discuss when an in-process bus is genuinely the right choice (decoupling within a service, transactional event listeners, modular monoliths) and when promoting to a broker is necessary (cross-service, durability, replay)
Interview-ready guide
Interview answer
The terminology is loose but the practical distinction is clear.
- A message broker is cross-process infrastructure — Kafka, RabbitMQ, ActiveMQ, SQS — that sits between services and persists messages.
- A message bus or event bus is an in-process publish-subscribe library — Spring's
ApplicationEventPublisher, GuavaEventBus, MediatR in .NET — where publisher and subscribers are objects in the same JVM.
Brokers:
- Network hop, persistent, survive consumer downtime
- Configurable delivery guarantees (at-least-once, exactly-once)
- Required for any messaging that crosses a service boundary
- Cost: a separate cluster to deploy, monitor, and secure
In-process buses:
- Method invocation, no network, no persistence
- Lost on JVM crash unless you use the outbox pattern alongside
- Default in Spring is synchronous in the caller thread; switch to
@Asyncfor true async - Excellent for decoupling within a service — metrics, audit, cache invalidation
- Killer feature:
@TransactionalEventListenerlets handlers run before or after the publisher's transaction commits, transactionally bound to it
Event bus vs message bus:
- Practically synonyms; some authors reserve 'event bus' for event-driven (publish/subscribe with unrelated consumers) and 'message bus' for command-style (one publisher addresses one handler)
- The same in-process tools serve both roles
When to choose each:
- In-process bus when the publisher and all subscribers live in the same Spring application — modular monoliths, single-service decoupling
- Broker when subscribers live in another service, when events must survive crashes, when you need replay, or when throughput exceeds what in-process listeners can absorb
- Hybrid is the norm: the in-process bus fires local side effects, and the same logical event goes out to Kafka via the Outbox pattern for cross-service delivery
Anti-pattern:
- Putting every internal method call through Kafka because 'events are good' — extreme operational cost, multi-ms latency between objects, observability nightmares
- Kafka is for inter-service integration; in-service decoupling belongs on the in-process bus
My default for a new Spring service is ApplicationEventPublisher plus @TransactionalEventListener for local concerns and the Outbox pattern fanning out to Kafka for cross-service consumers — that gives you both worlds without the anti-patterns of either extreme.
Message Broker vs Message Bus vs Event Bus
Direct Answer
A message broker is a separate infrastructure component (RabbitMQ, ActiveMQ, Kafka) that sits between services and routes/persists messages — it's a network-hop and is the default for cross-process messaging. A message bus is an in-process publish-subscribe abstraction (Spring's ApplicationEventPublisher, Guava EventBus, .NET MediatR) — handlers are JVM objects, no network involved, and a crash loses unhandled events. An event bus is usually a synonym for message bus but used in the context of event-driven (rather than command/RPC) communication; the same in-process tools serve both roles. The choice is really 'cross-process broker' vs 'in-process bus' — the broker/bus terminology only adds confusion when used loosely.
Code Example
In-process bus — Spring ApplicationEventPublisher + @TransactionalEventListener:
// Publisher — domain code in the order service
@Service
public class OrderService {
private final ApplicationEventPublisher events;
private final OrderRepository orders;
@Transactional
public Order place(PlaceOrderCommand cmd) {
Order o = orders.save(Order.from(cmd));
events.publishEvent(new OrderPlaced(o.getId(), o.getCustomerId(), o.getTotal()));
return o;
}
}
// Subscriber 1 — same JVM, same transaction context
@Component
public class InventoryReservationListener {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void on(OrderPlaced e) {
// Runs in the same JVM after the OrderService transaction commits.
// If THIS service crashes before this method runs, the event is GONE.
}
}
// Subscriber 2 — same JVM
@Component
public class OrderMetricsListener {
@EventListener
public void on(OrderPlaced e) {
Metrics.counter("orders.placed").increment();
}
}
Cross-process broker — Spring Kafka:
@Service
public class OrderService {
private final KafkaTemplate<String, OrderPlaced> kafka;
private final OrderRepository orders;
private final OutboxRepository outbox;
@Transactional
public Order place(PlaceOrderCommand cmd) {
Order o = orders.save(Order.from(cmd));
// Outbox: same DB transaction guarantees we don't lose the event on crash.
outbox.save(OutboxEvent.of("OrderPlaced", o.getId(), serialize(o)));
return o;
}
}
// Subscribers can be in ANY service / language reachable to Kafka.
@Component
public class InventoryService {
@KafkaListener(topics = "order-events", groupId = "inventory-svc")
public void on(OrderPlaced e) {
// Different process. Survives a crash of the order service.
}
}
Both produce the same logical decoupling — the order service doesn't know who reacts to its events. The in-process version is two annotations and zero infrastructure; the broker version requires Kafka, an outbox table, a relay process, and a topic schema. Use the in-process bus until the answer to 'who else needs to react to this event?' includes 'a different service'.
Common Mistakes & Pitfalls
Treating Spring ApplicationEventPublisher as a substitute for Kafka — it's in-process only, has no durability, and crashes lose unhandled events
Check your understanding
Answer these without looking back at the article.
- When is `@TransactionalEventListener(AFTER_COMMIT)` the right choice over a broker?
- How does the Outbox pattern bridge the in-process bus and a broker?
- Why is using Kafka for in-process method calls an anti-pattern?
- What does Spring's `ApplicationEventPublisher` lose if the JVM crashes between publish and handler?
Related interview prep categories
2Want a guided curriculum order?
These modules are also sequenced in our flagship backend interview track — open it when you prefer an ordered path instead of browsing by topic.