Course contentsShow
← Node.js
Lesson 244 of 1,9803. Asynchronous Patterns and Flow ControlFree lesson

Event-Driven Dependency Injection

You will use an event-based approach to resolve dependencies between decoupled application modules.

In this lesson, you will learn how to use an EventEmitter to resolve dependencies between modules without them needing to import each other directly.

The Problem: Tight Coupling

Usually, in Dependency Injection (DI), we pass a service directly into a constructor: new DatabaseService(config). However, if your application is highly modular or asynchronous, your services might not be ready at the exact moment you need them. If Module A requires Module B, but Module B is still initializing, you end up with undefined errors or complex timing issues.

The Solution: Event-Driven Resolution

Instead of passing dependencies manually, we use an EventEmitter as a central "Registry." Modules declare what they need by listening for specific events, and the Provider modules emit those dependencies once they are ready. This decouples the timing of the dependency from the definition of the dependency.

Imagine a Database module that needs to signal to the rest of the app that it is connected:

const EventEmitter = require('events');
const registry = new EventEmitter();

// The Provider: Announces availability
const db = { query: (q) => console.log(`Executing: ${q}`) };
setTimeout(() => {
  registry.emit('db-ready', db);
}, 1000);

// The Consumer: Waits for the dependency to exist
registry.once('db-ready', (database) => {
  database.query('SELECT * FROM users');
});

Why use this?

  1. Asynchronous Readiness: You don't care when the database starts; your consumer will simply trigger whenever the db-ready event fires.
  2. Reduced Circular Dependencies: Since modules don't require each other directly to get their dependencies, you avoid the messy "circular dependency" errors common in large Node.js projects.
  3. Flexibility: You can swap the Database module for a MockDatabase module during testing without changing a single line of code in the consumer—you only change which module emits the db-ready event.

By shifting from "passing" dependencies to "broadcasting" them, your architecture becomes much more resilient to the asynchronous nature of Node.js.

Key Takeaway: Event-Driven Dependency Injection allows modules to remain decoupled by using an Event Emitter as a central bus where providers broadcast their readiness and consumers wait for their required services to arrive.