---
term: 'Event-Driven Architecture (EDA)'
seoTitle: 'Event-Driven Architecture: Events vs. Commands, Choreography'
headline: 'What is Event-Driven Architecture (EDA)?'
slug: event-driven-architecture
category: backend-compute
shortDefinition: 'Event-driven architecture is a design style where services announce immutable facts as events, and consumers react independently.'
relatedTerms:
  - pub-sub-pattern
  - webhooks
  - database-triggers-beforesave-aftersave
  - microservices-vs-monolith
contrastsWith:
  - api-orchestration
aboutTerms:
  - 'Events vs. Commands'
  - 'Choreography'
  - 'Event Sourcing'
faq:
  - question: 'What is event-driven architecture in simple terms?'
    answer: 'Components announce that something happened — an event — instead of calling each other, and any interested component reacts on its own schedule. Broadcast instead of phone call: the announcer''s job ends at the announcement, and listeners come and go without the announcer changing.'
  - question: 'What exactly is an event?'
    answer: 'An immutable record of something that already happened, named in the past tense — OrderPlaced, PaymentFailed — carrying a payload plus metadata like an ID and timestamp. Because it is a fact about the past, consumers cannot decline it; they can only decide how to react.'
  - question: 'What is the difference between an event and a command?'
    answer: 'Intention. A command is an imperative aimed at one recipient that may refuse it — ShipOrder. An event is a past-tense fact broadcast to whoever cares — OrderShipped. The trap is the "passive-aggressive command": an event secretly expecting one specific consumer to act, which is a command wearing a costume.'
  - question: 'What is the difference between event-driven and request-response?'
    answer: 'Coupling in time. Request-response is synchronous — the caller waits, needs the answer, and fails if the callee is down. Events are fire-and-forget — resilient and decoupled, but eventually consistent. The rule: if the caller needs the result to continue, request-response; if the producer''s job is done before the reactions begin, events.'
  - question: 'What is the difference between EDA and pub/sub?'
    answer: 'Level. Pub/sub is a messaging pattern — one delivery mechanism, publishers to topics to subscribers. EDA is the architectural style that typically rides on it, along with queues and streams. Pub/sub is plumbing; event-driven is the design philosophy the plumbing serves.'
  - question: 'What are event notification, event-carried state transfer, and event sourcing?'
    answer: 'Fowler''s three distinct patterns hiding under one name. Notification: thin events, consumers call back for details. State transfer: events carry the full data, so consumers keep local copies. Sourcing: a persistence choice — store every change as an event and rebuild state by replay. The third is how one system stores data, not how systems talk.'
  - question: 'What is the difference between choreography and orchestration?'
    answer: 'Choreography: services react to each other''s events, no central brain — maximal decoupling, but the workflow exists nowhere explicitly. Orchestration: a coordinator commands each step — visible and debuggable, at the cost of a central coupling point. Rule: orchestrate when someone must own the outcome; choreograph when producers genuinely don''t care what happens next.'
  - question: 'How do you handle duplicate and out-of-order events?'
    answer: 'By design, not by hope: brokers deliver at-least-once, so consumers must be idempotent — dedupe by event ID, or write handlers whose repetition is harmless ("set status to shipped," never "toggle"). Ordering is guaranteed only per partition or key; architectures that require global order are fighting their own transport.'
  - question: 'When should you use event-driven architecture?'
    answer: 'When one fact has several independent consumers, when load is spiky and buffering helps, when systems must integrate without knowing each other, or when an audit trail of changes has value. When not: simple CRUD, reads, operations needing immediate strong consistency, and teams without distributed-systems appetite.'
  - question: 'What are the drawbacks of EDA?'
    answer: 'Debugging without a call stack — correlation IDs become a day-one requirement; eventual consistency surfacing in UX; schema evolution across independent consumers; duplicate delivery; and a broker to operate. The costs are real and front-loaded, which is why the architecture earns its keep only where its benefits compound.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'What do you mean by "Event-Driven"? — Martin Fowler'
    url: 'https://martinfowler.com/articles/201701-event-driven.html'
  - name: 'Enterprise Integration Patterns — Messaging'
    url: 'https://www.enterpriseintegrationpatterns.com/patterns/messaging/'
  - name: 'Saga pattern — microservices.io'
    url: 'https://microservices.io/patterns/data/saga.html'
  - name: 'CloudEvents — CNCF specification'
    url: 'https://cloudevents.io/'
  - name: 'Event-driven architecture — Wikipedia'
    url: 'https://en.wikipedia.org/wiki/Event-driven_architecture'
cta:
  title: 'Event-driven from day one'
  text: 'On Back4app every save is an event: afterSave triggers react server-side, Live Queries push facts to clients, webhooks carry them to other systems, and jobs absorb the slow work — everyday EDA, no broker to run.'
  linkText: 'Start building free'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-07-30'
translationKey: event-driven-architecture
---

**Event-driven architecture is a design style where services announce immutable facts as events, and consumers react independently.** The precision the vendor pages skip lives in two words: an event is an *immutable, past-tense* fact — `OrderPlaced`, not `PlaceOrder` — and the producer neither knows nor cares who reacts. That indifference is the architecture: consumers are added, removed, and broken without the producer changing a line, which is [decoupling](/glossary/decoupled-architecture/) at its most literal. And EDA is a spectrum, not a religion — a single [webhook](/glossary/webhooks/) is event-driven; so is a streaming estate; most healthy systems are hybrids.

## Key takeaways

| Question | Answer |
| --- | --- |
| An event is | An immutable, past-tense fact — consumers can react, never decline |
| Event ≠ command | Facts broadcast to whoever cares vs. imperatives aimed at one handler |
| The decision rule | Producer's job done before reactions begin → events; caller needs the answer → request-response |
| The three flavors | Notification · event-carried state · event sourcing (a *storage* choice) |
| The standing bill | Idempotent consumers, correlation IDs, eventual-consistency UX |

## One fact, many reactions

**JavaScript:**

```javascript
// JavaScript — Cloud Code (cloud/main.js)
// Everyday EDA: the write IS the event; consumers react independently
Parse.Cloud.afterSave('Order', async (req) => {
  const order = req.object;
  const becamePlaced = order.get('status') === 'placed' &&
    req.original?.get('status') !== 'placed';
  if (!becamePlaced) return;

  // Consumers of one fact — none knows about the others:
  await Parse.Cloud.httpRequest({          // fulfillment system (webhook)
    method: 'POST', url: process.env.FULFILL_WEBHOOK,
    body: { event: 'order.placed', id: order.id },
  });
  await enqueueConfirmationEmail(order);   // async job queue
  // Live Query pushes the change to every open dashboard automatically
});
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// The client as event consumer: subscribe to facts, react on arrival
final orders = QueryBuilder<ParseObject>(ParseObject('Order'))
  ..whereEqualTo('status', 'placed');
final sub = await LiveQuery().client.subscribe(orders);
sub.on(LiveQueryEvent.create, (order) => showNewOrder(order));
// Nobody called this screen — it REACTED to an event, like every
// other consumer of "order placed": fulfillment, email, analytics.
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// The client as event consumer: subscribe to facts, react on arrival
let orders = Order.query("status" == "placed")
let sub = try await orders.subscribe()
sub.handleEvent { _, event in
    if case .created(let order) = event { showNewOrder(order) }
}
// Nobody called this screen — it REACTED to an event, like every
// other consumer of "order placed": fulfillment, email, analytics.
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// The client as event consumer: subscribe to facts, react on arrival
val orders = ParseQuery.getQuery<ParseObject>("Order")
orders.whereEqualTo("status", "placed")
val sub = ParseLiveQueryClient.Factory.getClient().subscribe(orders)
sub.handleEvent(SubscriptionHandling.Event.CREATE) { _, order ->
    showNewOrder(order)
}
// Nobody called this screen — it REACTED to an event, like every
// other consumer of "order placed": fulfillment, email, analytics.
```

One fact — *an order became placed* — and four consumers, none aware of the others: a fulfillment system notified by webhook, an email job enqueued, analytics counting, dashboards updating over a live subscription. Adding a fifth consumer changes nothing upstream. That is the whole pitch, in one save.

```mermaid
flowchart LR
  accTitle: Producers, broker, and independent consumers in event-driven architecture
  accDescr: A producer emits an immutable past-tense event to a broker. The broker delivers copies to independent consumers, including a fulfillment service, an email job, analytics, and client dashboards. Consumers can be added or fail independently without the producer changing, and each consumer must handle duplicates idempotently.
  P["Producer<br/>order service"] -->|"OrderPlaced<br/>(immutable fact)"| B["Broker / channel<br/>topic, stream, or trigger"]
  B --> C1["Fulfillment<br/>(webhook)"]
  B --> C2["Email job<br/>(queue)"]
  B --> C3["Analytics"]
  B --> C4["Dashboards<br/>(live query)"]
  C5["New consumer,<br/>added Tuesday"] -.->|"subscribes — producer<br/>never knows"| B
```

## The vocabulary, untangled

The SERP's terminology chaos, resolved in one table — three message kinds by *intention*, not transport:

| | Command | Event | Query |
| --- | --- | --- | --- |
| Grammar | Imperative: `ShipOrder` | Past tense: `OrderShipped` | Interrogative |
| Audience | One handler | Whoever subscribed | One answerer |
| Can be refused | Yes | **No — it already happened** | n/a |
| Coupling | Sender knows the handler | Producer knows nobody | Caller waits |

[Fowler's](https://martinfowler.com/articles/201701-event-driven.html) warning deserves its quote: beware the *passive-aggressive command* — an "event" the producer emits while secretly requiring one specific consumer to act. If the flow breaks when that consumer doesn't respond, it was a command all along, and naming it an event just hid the coupling. (The infrastructure words — broker, router, bus, channel, topic — are near-synonyms for the middle box; the [pub/sub entry](/glossary/pub-sub-pattern/) covers that machinery, of which EDA is the generalizing architecture.)

## Event-driven vs. request-response

| | Request-response | Event-driven |
| --- | --- | --- |
| Time coupling | Caller waits — callee must be up | Fire-and-forget — consumers catch up |
| The answer | Returned to the caller | There is no answer, only reactions |
| Failure mode | Immediate, visible error | Resilient — and eventually consistent |
| Debugging | One call stack | Correlation IDs across hops |
| Right for | Reads, logins, payments — anything the caller *needs* | Facts with independent consumers |

The one-sentence rule: **if the caller needs the result to continue, request-response; if the producer's job is complete before any reaction begins, events.** Placing the order needs a synchronous answer ("did it succeed?"); everything after — email, fulfillment, analytics — is reactions to a fact, which is why real systems are hybrids by design rather than by compromise.

## Fowler's three flavors — and where event sourcing actually fits

| Pattern | The event carries | Consumers | Trade |
| --- | --- | --- | --- |
| Event notification | An ID and little else | Call back for details | Simple; callback traffic re-couples |
| Event-carried state transfer | The full relevant state | Keep local copies | No callbacks; duplication and staleness |
| Event sourcing | *Is* the system of record | Rebuild state by replay | Perfect audit; a heavyweight storage commitment |

The disambiguation that saves meetings: **event sourcing is how one system stores data; EDA is how systems talk.** They compose but neither requires the other. One more line the vendors blur: pub/sub *delivery* forgets — late subscribers miss what fired before they arrived — while *streaming* (the Kafka-style ordered log) remembers, letting consumers replay from any point; choose by whether history is part of the product.

## Choreography vs. orchestration

| | Choreography (EDA-native) | [Orchestration](/glossary/api-orchestration/) |
| --- | --- | --- |
| Coordination | Services react to each other's events | A coordinator commands each step |
| The workflow lives | Nowhere explicitly — emergent | In one visible, debuggable place |
| Coupling | Minimal | The orchestrator knows everyone |
| Failure handling | Compensating events, distributed | Central retry and error logic |

The mirror-image rule shared with the [orchestration entry](/glossary/api-orchestration/): **orchestrate when someone must own the outcome of a workflow; choreograph when producers genuinely don't care what happens next.** Long business transactions across services take the [saga](https://microservices.io/patterns/data/saga.html) shape either way — local transactions chained by events or by an orchestrator, undone by compensations rather than rollback.

## Everyday EDA — no broker required

The section the architecture centers never write: most app developers already run event-driven patterns without a message broker in sight. A [`beforeSave`/`afterSave` trigger](/glossary/database-triggers-beforesave-aftersave/) is a consumer of data-change events. A [webhook](/glossary/webhooks/) is an event crossing company boundaries over plain HTTP. A [live query](/glossary/real-time-live-queries/) is pub/sub with clients as subscribers. A [background job](/glossary/background-jobs-task-schedulers/) enqueued by a trigger is an event spawning async work. The code tabs above are exactly this stack — the write is the event, the trigger is the router, and the consumers are a webhook, a queue, and a subscription. Dedicated brokers (Kafka, RabbitMQ, NATS — with [CloudEvents](https://cloudevents.io/) as the standard envelope) earn their operational weight when event volume, replay, and cross-team contracts demand it; the *architecture* starts much earlier, and "do I need a broker?" usually answers itself: not yet.

## Common use cases

- **Order and payment flows** — one fact, many departments: the canonical e-commerce pipeline.
- **Cross-system integration** — services owned by different teams reacting without direct calls or shared deploys.
- **Real-time features** — dashboards, feeds, and [presence](/glossary/presence-online-status/) as client-facing event consumption.
- **Spiky workloads** — queues buffering bursts so consumers drain at their own pace.
- **Audit and replay** — streaming logs where "what happened, in order" is itself the product.

## Should you go event-driven? A decision matrix

| Situation | Lean |
| --- | --- |
| One fact, several independent consumers | Events — the home game |
| Caller needs the answer to proceed | Request-response, unapologetically |
| Workflow with an owner and an SLA | [Orchestration](/glossary/api-orchestration/) — command the steps |
| Reactions within one app (email on signup) | Triggers + jobs — everyday EDA |
| Cross-company notifications | [Webhooks](/glossary/webhooks/) |
| High-volume, replayable, multi-team streams | A real broker — now it's earned |

## Limitations and trade-offs

- **Debugging loses the call stack.** A request that fans into five async hops is traceable only by correlation IDs carried from the first event — retrofit is miserable; wire them from day one.
- **Eventual consistency reaches the UI.** The user updates a profile and the next screen shows the old name; either design the UX for it or keep that path synchronous.
- **Duplicates are contractual.** At-least-once delivery makes idempotent consumers mandatory — the [same contract](/glossary/background-jobs-task-schedulers/) queues impose, because it is the same machinery.
- **Schemas evolve under consumers' feet.** Events are contracts with unknown subscribers: additive changes only, versioned topics for breaks, and tolerant readers everywhere.
- **Emergent workflows hide.** Pure choreography means nobody can point at the business process; when auditors or on-call engineers need to, add the orchestrator.

## Event-driven patterns on Back4app

Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. The everyday-EDA section is its feature list read as architecture: every save is an event, [`afterSave` triggers](/glossary/database-triggers-beforesave-aftersave/) are server-side consumers with the full JavaScript ecosystem, [Live Queries](/glossary/real-time-live-queries/) fan facts out to clients over managed WebSockets, [webhooks](/glossary/webhooks/) carry them to external systems, and [background jobs](/glossary/background-jobs-task-schedulers/) absorb the slow reactions — the code tabs show the whole loop from one order save. The disciplines this article insists on remain yours (idempotent after-hooks, past-tense thinking, sync where the caller needs answers), while the broker-shaped infrastructure — fan-out, delivery, connection fleets — arrives as platform behavior, and graduating to a dedicated streaming broker later is an addition to this architecture, not a rewrite of it.
