ArchitectMind
  1. Home
  2. Tutorials
  3. Book 7 · Application Architecture & Distributed Systems
  4. Volume VI
  5. Event Fundamentals
Book 7 · Volume VI — Event-Driven & Asynchronous Architecture · Topic 1.1

Event Fundamentals

Event, producer, consumer and broker

19 min readStage 3 · Systems8 interview questions
Chapter
Chapter 1 — Event-Driven Architecture
Prerequisites
Volume V Topic 3.2 (asynchronous communication)
You will be able to
Define events precisely and distinguish them from commands and messages, design producers that publish reliably, build consumers that react independently, explain what a broker adds and choose among brokers, and describe the coupling benefits and costs of event-driven design
Covers
Event; Producer; Consumer; Broker
On this page
  1. 1. Concept / Definition
  2. 2. Why It Exists
  3. 3. Core Concepts
  4. 4. How It Works Internally — Decoupling Dimensions
  5. 4A. Deep Dive — The Costs of Events
  6. 5. APIs / Features / Syntax
  7. 6. Practical Examples
  8. 7. Common Problems & Mistakes
  9. 8. Performance & Production Considerations
  10. 9. Architecture & Design Considerations
  11. 10. Interview Questions & Answers

1Concept / Definition

In event-driven architecture (EDA), components communicate by publishing and reacting to events — immutable records that something happened. Producers publish events without knowing who will react; consumers subscribe and act independently; a broker stores and distributes events between them, decoupling both sides in time and location.

Event, producer, consumer, broker (TOC)
Figure 1 — Event, producer, consumer, broker (TOC)
TOC elementDefinition
EventAn immutable, past-tense fact about something that happened in a domain (OrderPlaced, PaymentCaptured), with an identity, a type, a time, a source and a payload
ProducerThe component that detects the fact — usually the owner of the state that changed — and publishes the event reliably, without depending on who consumes it
ConsumerA component that subscribes to event types it cares about and reacts by updating its own state, triggering work or publishing further events, at its own pace
BrokerMiddleware (Kafka, RabbitMQ, SNS/SQS, EventBridge) that durably accepts events from producers and delivers them to consumers, providing buffering, fan-out, ordering and sometimes replay
Table 1 — The four TOC elements

2Why It Exists

With synchronous calls, placing an order means calling billing, inventory, email and loyalty in the request path: every new reaction changes order-svc, and any slow service slows checkout. Publishing OrderPlaced once lets each reaction live in its own service.

Checkout latency: sync calls vs events (illustrative)
Figure 2 — Checkout latency: sync calls vs events (illustrative)

3Core Concepts

3.1 Event

Anatomy of an event
Figure 3 — Anatomy of an event
Event vs command
Figure 4 — Event vs command
JAVA
// A published (integration) event: immutable, past tense, versioned type
public record OrderPlaced(
        String eventId,          // unique, for idempotency
        String orderId,
        String customerId,
        List<Line> lines,
        long totalPaise,
        String currency,
        Instant occurredAt) {
    public record Line(String sku, int qty, long pricePaise) {}
}
PropertyRule
ImmutableNever "update" an event; publish a new one
Past tenseOrderPlaced, not PlaceOrder
IdentifiedeventId for dedupe; subject/key for ordering
TimestampedoccurredAt (business time) vs publish time
Self-describingType + schema version
Table 2 — Event properties

3.2 Producer

JAVA
// Reliable publishing: write state + outbox in one local transaction (Topic 3.2 of Vol V)
@Transactional
public OrderId place(PlaceOrder cmd) {
    Order order = orders.save(Order.place(cmd));
    outbox.save(OutboxRecord.of("orders.events.v1", order.id().value(),
            "shopnow.order.placed.v1", mapper.toJson(OrderPlaced.from(order))));
    return order.id();
}
// A relay (Debezium CDC or polling publisher) sends outbox rows to Kafka
Producer responsibilityWhy
Publish only after state commitsNo events for rolled-back changes
Never lose eventsOutbox / transactional publishing
Choose keyOrdering per entity (Topic 2.3)
Own the schemaVersioned, compatible evolution
Don't wait for consumersTemporal decoupling
Table 3 — What producers must do

3.3 Consumer

Publishing and consuming an event
Figure 5 — Publishing and consuming an event
JAVA
@KafkaListener(topics = "orders.events.v1", groupId = "billing-svc")
void on(OrderPlaced e) {
    // idempotent
    invoices.createDraftIfAbsent(e.orderId(), e.customerId(), e.totalPaise());
}
Consumer responsibilityWhy
Own its reaction and stateAutonomy
Tolerate duplicatesAt-least-once delivery (Topic 3.3)
Tolerate unknown fields/typesProducer evolution
Handle failures without blockingRetries, DLT (Topic 3.4)
Track its own progressConsumer group offsets
Table 4 — What consumers must do

3.4 Broker

What the broker provides
Figure 6 — What the broker provides
Broker choices
Figure 7 — Broker choices

4How It Works Internally — Decoupling Dimensions

DimensionSynchronous callEvent via broker
TimeBoth must be upConsumer may be down; catches up later
LocationCaller knows addressProducer knows only the topic
KnowledgeCaller knows callee's APIProducer doesn't know consumers
LoadCaller's rate hits calleeBroker buffers; consumers pull
ResultCaller gets responseNo response; outcomes are new events
Table 5 — What events decouple

4ADeep Dive — The Costs of Events

CostMitigation
Eventual consistencyDesign UX and processes for it (Chapter 5)
Hidden flowsEvent catalog, tracing (Topic 4.1)
Duplicates and reorderingIdempotent consumers, keys (Ch 3, 2.3)
Schema couplingRegistry + compatibility rules (Chapter 6)
Debugging across hopsTrace context in headers
Operating a brokerManaged services (MSK, EventBridge)
Table 6 — Events are not free

5APIs / Features / Syntax

NeedTooling
Produce/consumeSpring Kafka KafkaTemplate, @KafkaListener
Binder abstractionSpring Cloud Stream (Kafka, Rabbit, SQS)
Reliable publishOutbox + Debezium, Spring Modulith externalisation
Envelope standardCloudEvents SDK for Java
Managed brokersAmazon MSK, SNS/SQS, EventBridge
Table 7 — Toolkit

6Practical Examples

6.1 Removing synchronous side effects

ShopNow's checkout called four services after saving the order. Replacing those calls with one OrderPlaced event via outbox cut checkout p99 from 640 ms to 95 ms and made email outages invisible to customers.

6.2 Adding a consumer without touching the producer

The data team needed order data for fraud models. They subscribed a new consumer group to orders.events.v1; order-svc required no change or deployment.

6.3 Worked example — naming

BadGood
UpdateOrderOrderAddressChanged
Table 8 — Facts, past tense

Scenario. ShopNow's order service published an event called UpdateOrder whenever an address changed. Consumers could not tell what had actually happened, and some treated it as an instruction to update their own data.

Walkthrough. Events record facts that have already happened, so they are named in the past tense with business meaning. UpdateOrder sounds like a command and says nothing about what changed. OrderAddressChanged states a specific fact, carries the old and new address, and lets each consumer decide how to react: shipping recalculates the route, while billing ignores it.

Result. Consumers subscribed only to the facts they cared about, and the event catalogue became readable by non-developers.

Common mistake. Using generic names such as OrderUpdated with a changed-fields list. Consumers must inspect the payload to understand what happened, and the business meaning is lost.

Takeaway. Name events as specific business facts in the past tense. If the name sounds like an instruction, it is probably a command in disguise.

6.4 Worked example — command disguised as event

SmellFix
SendEmailEventPublish OrderPlaced; email-svc decides
Table 9 — Producer shouldn't direct

Scenario. ShopNow's order service published SendEmailEvent after each order. When marketing wanted a different email flow, the orders team had to change their code, although email was not their responsibility.

Walkthrough. An event that tells a specific consumer what to do is a command disguised as an event, and it couples the producer to that consumer. The fix was to publish the business fact, OrderPlaced, and let the email service decide which emails to send. Other services, such as loyalty and analytics, now react to the same fact without any change to the producer.

Result. Marketing changed the email flow inside the email service alone, and the orders team stopped receiving requests about emails.

Common mistake. Adding one new event per consumer need, such as SendSmsEvent and SendPushEvent. The producer slowly becomes an orchestrator of other teams' work.

Takeaway. Producers should publish facts, not instructions. Each consumer decides what to do, which keeps the producer independent of downstream behaviour.

6.5 Worked example — CloudEvents headers

HeaderValue
ce_typeshopnow.order.placed.v1
ce_source/order-svc
Table 10 — Standard envelope

Scenario. ShopNow's teams each designed their own event envelope, with different names for the event type, the source and the timestamp. Every consumer needed custom parsing code for every producer.

Walkthrough. CloudEvents is a standard envelope for event metadata. Fields such as ce_type, here shopnow.order.placed.v1, ce_source, here /order-svc, ce_id and ce_time travel as message headers, while the business payload stays in the body. Libraries for Kafka, HTTP and AWS understand these headers, so routing, logging and tracing work the same way for every event.

TEXT
ce_specversion: 1.0
ce_type:   shopnow.order.placed.v1
ce_source: /order-svc
ce_id:     6f1c2e4a-...
ce_time:   2026-11-08T10:15:30Z
content-type: application/json

Result. Consumers handled events from all producers with one generic envelope parser, and tooling could filter events by type without reading their payloads.

Common mistake. Putting envelope metadata inside each team's payload under different names. Generic tools cannot route or trace events without understanding every schema.

Takeaway. Standardise event metadata with CloudEvents or a similar envelope, and include a version in the event type. Consistency across producers saves effort in every consumer.

6.6 Worked example — publish after commit

ApproachRisk
send() inside @TransactionalEvent for rolled-back tx
OutboxSafe
Table 11 — Dual-write problem

Scenario. ShopNow's order service sent OrderPlaced to Kafka inside a @Transactional method. When the database insert later failed and rolled back, consumers had already received an event for an order that did not exist.

Walkthrough. Writing to the database and sending to Kafka are two separate systems, so they cannot commit atomically: this is the dual-write problem. With the transactional outbox pattern, the service writes the event to an outbox table in the same database transaction as the order. A relay, such as Debezium or a scheduled publisher, then sends outbox rows to Kafka after commit, retrying until it succeeds.

SQL
BEGIN;
INSERT INTO orders (id, customer_id, total) VALUES (:id, :customer, :total);
INSERT INTO outbox (id, aggregate_id, type, payload)
     VALUES (:eventId, :id, 'OrderPlaced', :json);
COMMIT;   -- both rows or neither

Result. Events are now published only for committed orders, and no event is lost if Kafka is briefly unavailable.

Common mistake. Sending the event first and then saving, hoping both succeed. A crash between the two steps publishes events for data that was never stored, or the reverse.

Takeaway. Never send events directly inside a database transaction. Use an outbox so the state change and the event are committed together.

6.7 Worked example — fan-out

Consumers of OrderPlaced4 groups
Table 12 — One publish, many reactions

Scenario. When ShopNow placed an order, four teams needed to react: payments, inventory, email and analytics. Previously, the order service called each of them directly.

Walkthrough. With an event, the order service publishes OrderPlaced once, and four consumer groups each receive their own copy from the broker. Each group processes at its own speed and can be scaled independently. Adding a fifth consumer, such as loyalty points, requires no change to the order service.

Result. The order service's code shrank, checkout no longer waited for four downstream calls, and the loyalty team joined without involving the orders team.

Common mistake. Letting one slow consumer share a consumer group with others. Each reacting service needs its own group, so it reads every event at its own pace.

Takeaway. Use events for fan-out: one publication, many independent reactions. The producer stays the same however many consumers are added.

6.8 Worked example — consumer down

Email down 20 minEffect
Events wait in KafkaSent when back
Table 13 — Temporal decoupling

Scenario. ShopNow's email service was down for 20 minutes during a deployment problem. In the old synchronous design, checkout would have failed or skipped confirmation emails.

Walkthrough. Because OrderPlaced events are stored in Kafka, the email service simply resumed from its last committed offset when it came back. The events had waited in the topic, and all confirmation emails were sent, just 20 minutes late. Checkout itself was never affected, because it did not depend on the email service being available.

Result. Customers received their emails slightly delayed, nobody lost an order, and no manual recovery was needed.

Common mistake. Setting topic retention shorter than the longest realistic outage. Events expire before the consumer returns, and data is silently lost.

Takeaway. Events give temporal decoupling: producers and consumers do not need to be available at the same time. Size topic retention to cover the longest outage you expect.

6.9 Worked example — RabbitMQ vs Kafka

NeedChoice
Replay, ordering, high volumeKafka
Complex routing, per-message TTLRabbitMQ
Table 14 — Fit

Scenario. ShopNow's platform team had to choose a broker for two new needs: an order event stream that analytics wanted to replay, and a notification system with complex routing rules and per-message expiry.

Walkthrough. The two needs favour different brokers. Kafka stores events durably in an ordered log, supports replay from any offset and handles very high volume, which suits the order stream. RabbitMQ offers flexible routing with exchanges and bindings, per-message time-to-live and priorities, which suits notifications.

Result. ShopNow used Kafka for the order stream and RabbitMQ for notifications, each where it fits best, and documented the choice in an ADR.

Common mistake. Choosing a broker because it is already installed, without checking replay, ordering or routing needs. The wrong fit leads to workarounds that are harder to run than a second broker.

Takeaway. Choose a broker by the properties you need: replay, ordering and volume point to Kafka; rich routing and per-message handling point to RabbitMQ.

6.10 Worked example — EventBridge

UseWhy
SaaS/AWS service events to LambdasManaged rules
Table 15 — AWS-native

Scenario. ShopNow wanted to react to AWS service events, such as an S3 upload or a scheduled rule, and to events from a SaaS partner, without running more infrastructure.

Walkthrough. Amazon EventBridge is a managed event bus. It receives events from AWS services, SaaS integrations and custom applications, and routes them to targets such as Lambda functions using declarative rules that match on event content. There are no brokers to operate, and rules can be managed as infrastructure code.

JSON
{
  "source": ["aws.s3"],
  "detail-type": ["Object Created"],
  "detail": { "bucket": { "name": ["shopnow-supplier-feeds"] } }
}

Result. Supplier feed uploads now trigger a Lambda that starts the import, without any polling or custom infrastructure.

Common mistake. Pushing very high-volume streams through a managed event bus designed for routing. Costs and throughput limits become problems that a streaming platform would avoid.

Takeaway. Use managed event buses such as EventBridge for cloud-service and SaaS events. They remove operational work, though they are less suited to high-volume streams that need replay.

6.11 Worked example — business time

FieldMeaning
occurredAtWhen order was placed
Table 16 — Not broker time

Scenario. A finance report used the Kafka message timestamp as the time each order was placed. Orders replayed after an outage showed the wrong day.

Walkthrough. The broker's timestamp records when the message was written to the topic, which can differ from when the business event occurred, especially after retries, outbox delays or replays. The event payload should carry occurredAt, the business time the order was placed, set by the producer. Reports and business logic use occurredAt, not broker time.

Result. Reports became correct even for events published late, and replays no longer distorted daily figures.

Common mistake. Deriving business dates from broker or consumer clocks. Replays and delays then change reports, even though the business facts did not change.

Takeaway. Every event should carry its business time in the payload. Broker timestamps describe transport, not business facts.

6.12 Worked example — no response expected

Need a result?Use request/reply or command
Table 17 — Events aren't RPC

Scenario. A developer published a CheckStock event and then waited for a StockChecked event to decide whether checkout could continue. Latency and timeouts made checkout unreliable.

Walkthrough. Events are one-way notifications, not remote procedure calls. When the producer needs a result to continue, it should call the service synchronously with request/reply, or send a command whose reply is handled asynchronously by design. Here, checkout needed the answer immediately, so a synchronous stock-check API with a timeout and fallback was the right choice.

Result. Checkout latency became predictable, and the event stream was used only for facts that other services react to later.

Common mistake. Waiting synchronously for a reply event inside an HTTP request. Latency becomes unpredictable, and timeouts leave the outcome unclear.

Takeaway. Do not use events as disguised RPC. If you need a result now, use request/reply; use events for facts that others may react to in their own time.

6.13 Worked example — consumer reacts with event

ConsumerEmits
inventoryStockReserved
Table 18 — Chains (Topic 4.1)

Scenario. After receiving OrderPlaced, ShopNow's inventory service reserves stock. Other services, such as shipping, need to know when the reservation succeeds.

Walkthrough. A consumer that changes its own state can publish its own fact. Inventory consumes OrderPlaced, reserves the stock in its database and publishes StockReserved, again through its outbox. Shipping reacts to StockReserved. This creates a chain of reactions in which each service owns its step, without a central coordinator.

Result. Each step is independent and visible as an event. Topic 4.1 discusses when such choreography becomes hard to follow and orchestration is better.

Common mistake. Publishing follow-up events directly instead of through the consumer's own outbox. The same dual-write problem then appears at every step of the chain.

Takeaway. Consumers can become producers of new facts, forming event chains. Keep each step's ownership clear and monitor the whole chain end to end.

6.14 Worked example — trace context

HeaderPurpose
traceparentEnd-to-end traces
Table 19 — Observability

Scenario. An order was paid but never shipped. Logs existed in five services, but nobody could link them to follow the order across the asynchronous flow.

Walkthrough. Distributed tracing needs the trace context to travel with each event. Adding the W3C traceparent header to every message, which Spring Kafka and OpenTelemetry do automatically when observation is enabled, lets the tracing system connect the producer's span with each consumer's processing span.

YAML
spring:
  kafka:
    template:
      observation-enabled: true
    listener:
      observation-enabled: true

Result. The stuck order's trace showed that shipping had received StockReserved but failed on a validation error, which was fixed within the hour.

Common mistake. Starting a new trace in each consumer. The flow appears as unrelated fragments, and the cause of a stuck process cannot be found.

Takeaway. Propagate trace context through events as well as HTTP calls. Without it, asynchronous flows are almost impossible to debug.

6.15 Worked example — event catalog

EntryContent
orders.events.v1Owner, schema, consumers
Table 20 — Discoverability (AsyncAPI)

Scenario. New teams at ShopNow did not know which events existed, who owned them or what their schemas were, so they asked around or subscribed to the wrong topics.

Walkthrough. An event catalogue lists every published topic with its owner, schema, version and consumers, for example orders.events.v1 owned by the orders team. ShopNow describes its event APIs with AsyncAPI documents stored next to the code, and generates the catalogue from them.

Result. Teams discovered events through the catalogue, and owners could see who would be affected by a schema change.

Common mistake. Documenting events only in a wiki page that nobody updates. Schemas drift, and consumers rely on fields that no longer exist.

Takeaway. Make events discoverable with a catalogue built from AsyncAPI or similar specifications. Events nobody can find are hard to reuse safely.

6.16 Worked example — ordering need

EventsKey
Order lifecycleorderId
Table 21 — Per-entity order

Scenario. Shipping occasionally received OrderCancelled before OrderPlaced for the same order and tried to cancel a shipment that did not exist yet.

Walkthrough. Kafka guarantees order only within a partition. The producer had used random keys, so events for one order landed in different partitions. Using the order ID as the message key sends all events for one order to the same partition, so consumers see them in the order they were published.

Result. Out-of-order processing for the same order stopped, while different orders still spread across partitions for parallelism.

Common mistake. Increasing partitions later without considering keys. Existing keys can move to different partitions, briefly breaking per-entity ordering.

Takeaway. Choose message keys by the ordering you need. Usually that is per entity, such as order ID, rather than global ordering.

6.17 Worked example — in-process events

ScopeMechanism
Within one serviceSpring ApplicationEvent / Modulith
Table 22 — No broker needed

Scenario. A team proposed introducing Kafka to decouple two modules inside the same Spring Boot application.

Walkthrough. Within one service, a broker adds infrastructure without much benefit. Spring's ApplicationEvent, or Spring Modulith's transactional event publication, gives in-process events with asynchronous listeners and retry through a publication registry. Kafka becomes useful when events must cross service boundaries or be replayed.

Result. The team used Modulith events inside the service and published only selected events externally, keeping operations simple.

Common mistake. Adding a broker for in-process decoupling. The team takes on operations, monitoring and failure modes it does not need.

Takeaway. Use in-process events for decoupling inside one service, and introduce a broker only when events must leave the process.

6.18 Worked example — cost of eventual consistency

UI after checkoutShows
"Invoice being prepared"Honest status
Table 23 — Design UX

Scenario. After checkout, customers saw "Invoice not found" for a few seconds, because the invoice was created asynchronously by billing in response to OrderPlaced.

Walkthrough. Eventual consistency is a user-experience issue as well as a technical one. The confirmation page was changed to show an honest status, "Your invoice is being prepared", and to update automatically when the invoice became available.

Result. Support tickets about missing invoices disappeared, although the technical delay was unchanged.

Common mistake. Showing errors such as "not found" for data that is simply not processed yet. Users assume something failed and contact support.

Takeaway. Design the user experience for eventual consistency. Showing pending states honestly is often better than trying to hide short delays.

6.19 Worked example — ADR

DecisionRationale
OrderPlaced via outbox to KafkaDecoupling, reliability
Table 24 — Recorded

Scenario. A new architect asked why ShopNow publishes OrderPlaced through an outbox to Kafka instead of calling billing and shipping directly.

Walkthrough. The decision is recorded in an ADR. It states the context, the decision to publish OrderPlaced through an outbox to Kafka, and the rationale: decoupling producers from consumers, reliable delivery of events for committed orders, and replay for analytics. It also records the consequences: eventual consistency and the need for idempotent consumers.

Result. The new architect understood the reasoning in minutes, and later discussions about changing the design started from the recorded trade-offs.

Common mistake. Recording only the decision and its benefits. Without consequences such as eventual consistency, later teams are surprised by its costs.

Takeaway. Record event-driven design decisions in ADRs, including their costs. The reasons behind an architecture are as important as the architecture itself.

6.20 Worked example — review checklist

Check✔
Events are facts✔
Reliable publish✔
Idempotent consumers✔
Catalogued, schema'd✔
Table 25 — EDA review

Scenario. ShopNow reviews every new event-driven integration with a short checklist before it goes to production.

How to use it. Each item addresses a common failure. Events are facts, not commands in disguise. Publication is reliable, through an outbox rather than dual writes. Consumers are idempotent, because brokers can deliver duplicates. Events are catalogued with an owner and a registered schema. A design that cannot tick every item is sent back with specific actions.

Effect. Duplicate-processing incidents and lost events dropped sharply after the checklist was introduced.

Common mistake. Treating the checklist as a formality ticked by the author alone. It works best when a reviewer checks each item with evidence.

Takeaway. A short, specific checklist catches most event-driven design mistakes before they reach production.

6.21 Worked example — interview answer

ElementAnswer
EventImmutable fact
ProducerPublishes reliably
ConsumerReacts independently
BrokerStores & distributes
Table 26 — Structured answer

Scenario. In interviews, ShopNow asks candidates to explain the basic elements of event-driven architecture.

How to answer. A clear answer defines four elements and how they fit. An event is an immutable fact about something that happened. A producer publishes events reliably, without knowing consumers. A consumer reacts independently and must be idempotent. A broker stores events and distributes them, enabling temporal decoupling and, in Kafka's case, replay.

How to finish. Adding one real example, such as OrderPlaced triggering payment and email, and naming one cost, eventual consistency, makes the answer convincing.

Common mistake. Listing technologies such as Kafka and RabbitMQ instead of explaining the concepts. Interviewers look for understanding of roles and trade-offs, not product names.

Takeaway. Explain each element in one sentence, connect them with a real flow and mention the main trade-off.

7Common Problems & Mistakes

  • Commands disguised as events.
  • Publishing before commit (dual writes).
  • Assuming exactly-once delivery.
  • Publishing internal models.
  • Expecting responses from events.
  • No catalog of events and consumers.

8Performance & Production Considerations

AspectGuideline
ReliabilityOutbox, acks=all, idempotent producer
ThroughputBatching, compression
ConsumersScale via groups/partitions
VisibilityTracing, catalog
OperationsManaged brokers, monitoring lag
Table 27 — Operational guidelines

9Architecture & Design Considerations

  • Publish facts owned by the producer.
  • Keep producers ignorant of consumers.
  • Design for eventual consistency and duplicates.
  • Choose brokers by replay, ordering and routing needs.

10Interview Questions & Answers

Q1Basic

What is an event?

An event is an immutable record that something happened in the business, such as OrderPlaced or PaymentCaptured. It is named in the past tense and carries the facts about the occurrence: identifiers, relevant data and the time it happened. Because it describes the past, it cannot be rejected or changed; consumers can only decide how to react. Events are published without knowing who will consume them, which decouples producers from consumers. They differ from commands, which ask a specific receiver to do something and may be refused.

Q2Basic

What does a broker do?

A broker sits between producers and consumers. It receives events from producers, stores them durably and delivers them to consumers, so the two sides never talk directly and do not need to be available at the same time. Depending on the product, a broker also provides routing, such as topics and exchanges, ordering within partitions, retention and replay, consumer groups for scaling, and dead-letter handling. Kafka, RabbitMQ and Amazon EventBridge are common examples. Because many systems depend on it, a broker must be highly available, monitored and secured.

Q3Intermediate

How does an event differ from a command?

An event states a fact that has already happened, such as OrderPlaced, and is broadcast to any interested consumer; the producer does not know or care who reacts, and the fact cannot be rejected. A command asks one specific handler to perform an action, such as ReserveStock, is addressed to that handler, and may be rejected if the action is not possible. Commands usually go to queues; events usually go to topics. Mixing them, for example an event that expects one particular consumer to act, creates hidden coupling.

Q4Intermediate

Why should producers not know consumers?

If producers know their consumers, every new consumer or change in a consumer's needs requires changing the producer, which brings back the coupling events are meant to remove. When producers simply publish facts, new consumers such as analytics or loyalty can subscribe without the producer's involvement, and existing consumers can change their behaviour independently. The producer team can focus on its own domain. The trade-off is that the overall flow is less visible, so event catalogues, tracing and monitoring become important. In large organisations, this independence is what lets many teams evolve their services in parallel.

Q5Senior Engineer

How do you publish events reliably?

The core problem is the dual write: saving state to the database and sending the event to the broker are two separate operations that cannot commit atomically. With the transactional outbox pattern, the service writes the event into an outbox table in the same database transaction as the state change. A separate relay, for example Debezium reading the database log or a scheduled publisher, then sends outbox entries to the broker and marks them as sent, retrying until it succeeds. Events are thus published only for committed changes and never lost, though possibly duplicated, so consumers must be idempotent.

Q6Senior Engineer

What does temporal decoupling mean?

Temporal decoupling means producers and consumers do not need to be running at the same time. The producer publishes an event to the broker and continues; the broker stores it until the consumer is available and ready to process it. If the email service is down for twenty minutes, order events wait in the topic and are processed when it returns, and checkout is unaffected. This improves resilience and lets each side scale and deploy independently. The cost is eventual consistency: the consumer's view is updated later, and monitoring of consumer lag becomes essential.

Q7Technical Architect

When would you choose RabbitMQ over Kafka?

Choose RabbitMQ when you need flexible message routing and per-message handling rather than a replayable log. RabbitMQ's exchanges and bindings support routing by keys, patterns and headers; it offers per-message time-to-live, priorities, delayed delivery through plugins and straightforward work queues with acknowledgements. That suits task distribution, notifications and integration with many routing rules. Kafka is better when you need durable, ordered streams, very high throughput, long retention and the ability to replay events, such as event sourcing or analytics pipelines. Many organisations run both for different purposes.

Q8Solution Architect

What are the main costs of event-driven design?

Event-driven design trades immediate consistency and visible control flow for decoupling. Data becomes eventually consistent, so users and services see intermediate states. Business flows are spread across many consumers, which makes them harder to understand and debug without tracing and documentation. Brokers can deliver duplicates and, across partitions, out-of-order messages, so consumers must be idempotent and order-tolerant. Event schemas become long-lived contracts that need versioning and governance. And brokers are critical infrastructure that must be operated, monitored and secured. These costs are worthwhile when decoupling and scalability matter.

Newsletter

Get new tutorials every week

New ArchitectMind tutorials and interview questions, once a week. No spam, unsubscribe anytime.

Almost done! Check your inbox and click the confirmation link to complete your subscription.
Keep going in Book 7

Application Architecture & Distributed Systems

Volume I · Topic 1.1

Software Architecture

Architecture Fundamentals & Architectural Thinking

Volume I · Topic 1.2

Architecture Responsibilities

Architecture Fundamentals & Architectural Thinking

Volume I · Topic 1.3

Architect vs Developer

Architecture Fundamentals & Architectural Thinking

Volume II · Topic 1.1

Monolith Fundamentals

Application Architecture Styles & Patterns

Volume II · Topic 1.2

Types of Monoliths

Application Architecture Styles & Patterns

Volume II · Topic 1.3

Monolith Advantages

Application Architecture Styles & Patterns

Volume III · Topic 1.1

Domain-Driven Design

Domain-Driven Design & Business Boundaries

Volume III · Topic 1.2

Strategic vs Tactical DDD

Domain-Driven Design & Business Boundaries

Volume III · Topic 1.3

Domain Complexity

Domain-Driven Design & Business Boundaries

Volume IV · Topic 1.1

What Is a Distributed System?

Distributed Systems Fundamentals

Volume IV · Topic 1.2

Why Distributed Systems Exist

Distributed Systems Fundamentals

Volume IV · Topic 1.3

Distributed System Complexity

Distributed Systems Fundamentals

Volume V · Topic 1.1

What Makes a Microservice?

Microservices Architecture

Volume V · Topic 1.2

Service Granularity

Microservices Architecture

Volume V · Topic 1.3

Microservice Boundaries

Microservices Architecture

Volume VI · Topic 1.2

Event Types

Event-Driven & Asynchronous Architecture

Volume VI · Topic 1.3

Event-Carried State

Event-Driven & Asynchronous Architecture

Volume VII · Topic 1.1

Scalability

Resilience, Scalability & Distributed Data

Volume VII · Topic 1.2

Stateless Architecture

Resilience, Scalability & Distributed Data

Volume VII · Topic 1.3

Load Balancing

Resilience, Scalability & Distributed Data

Volume VIII · Topic 1.1

ADR

Architecture Decision-Making & System Evolution

Volume VIII · Topic 1.2

Architecture Decision Process

Architecture Decision-Making & System Evolution

Volume VIII · Topic 1.3

Reversible vs Irreversible Decisions

Architecture Decision-Making & System Evolution