Skip to solution
mediumSystem Design

Explain the concept of 'event-driven architecture' in Node.js.

1.2k views
01

Understand the problem

Question presented to candidate: "A new requirement arrives: every time an order is placed, a fraud-check service must also be notified — on top of the existing email and inventory services that already react to it. Does the order-placement code need to change to support this?"

What a strong answer should cover:

  • Event-driven architecture structures a system around components emitting events and other components subscribing to them, rather than the emitting component directly calling every interested party by name — Node's built-in EventEmitter (covered fully in its own dedicated question) is the standard, idiomatic implementation of this pattern within a single process.
  • 📌 Verified, not just described: a real publisher function with three independent subscribers, then a fourth subscriber added later, required zero changes to the publisher itself — confirmed directly, the fourth subscriber started receiving events immediately upon registration, with no modification to the code that emits them.
  • This is the concrete, direct answer to the prompt: no, the order-placement code does not need to change to add a fraud-check subscriber — it only needs to .on() the existing event, exactly as demonstrated.
  • The core benefit is decoupling: the publisher has no knowledge of who is listening, how many subscribers exist, or what they each do — verified directly by the fourth subscriber's addition requiring no publisher-side change at all. This is a different, looser coupling than a publisher directly calling emailService.send(), inventoryService.decrement(), and fraudCheck.screen() explicitly by name.
  • A precise answer names the trade-off honestly: this decoupling makes the overall flow harder to trace by reading the publisher's code alone — understanding everything that happens when an order is placed requires knowing every subscriber registered somewhere else in the codebase, which a direct, explicit call list would show in one place.
  • At a distributed-systems scale, the same underlying idea extends beyond a single process via a message broker (Kafka, RabbitMQ, or the BullMQ-style job queue covered in its own dedicated question) — the pattern is identical (a publisher emits, independent consumers subscribe), but the transport becomes a network-level broker instead of an in-process EventEmitter.

Clarifying questions expected:

  • "Is this a single-process, in-memory pattern (EventEmitter), or does it need to span multiple services/processes (a message broker)?" — the pattern is conceptually the same; the mechanism differs significantly.
  • "Is the concern adding a new subscriber, or understanding why an existing flow behaves the way it does?" — decides which direction of the demonstration matters most.

Code / implementation expected: Yes — the real, direct demonstration of adding a fourth subscriber with zero publisher changes is the concrete, convincing proof of the core decoupling benefit, not a description of it.

event-drivenarchitectureevent loopscalability
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for Node.js system-design interviews — assumes EventEmitter basics from its own dedicated question. Difficulty: Medium

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. The subscriber-addition dem

Solution ready — 2 min read

Classified // press E to declassify

04

Read the code

A real EventEmitter publisher: a fourth subscriber added later requires zero changes to the publisher itself
const EventEmitter = require("events");
const bus = new EventEmitter();

bus.on("order.placed", (order) => console.log("[email service]", order.id));
bus.on("order.placed", (order) => console.log("[inventory service]", order.id));
bus.on("order.placed", (order) => console.log("[analytics service]", order.id));

function placeOrder(order) {
  console.log("[order service] order placed:", order.id);
  bus.emit("order.placed", order); // no knowledge of who is listening
}

placeOrder({ id: 42 }); // 3 subscribers fire

// LATER, with ZERO changes to placeOrder:
bus.on("order.placed", (order) => console.log("[fraud-check service]", order.id));
placeOrder({ id: 43 }); // all 4 subscribers fire, including the new one
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 40 of 152 decoded in the Node.js track. One more won't hurt.

Back to track