Back to Research
Architecture2026-02-06·7 min read read

Event-Driven Architecture Without the Distributed Systems PhD

event-drivenarchitecturemessagingasync patterns
Event-Driven Architecture Without the Distributed Systems PhD

When developers hear "event-driven architecture," they picture Kafka clusters, microservices publishing to message brokers, and eventual consistency headaches. That version exists for companies processing millions of events per second. It is not what most applications need. But the underlying principles are valuable at any scale when implemented simply.

The simplest pattern is in-process events. A typed EventEmitter in your application lets modules communicate without direct dependencies. When a user places an order, the orders module emits OrderPlaced. The notifications module listens and sends a confirmation email. The analytics module updates metrics. The inventory module decrements stock. The orders module knows nothing about these consumers. This solves the "and also" problem: without events, all side effects end up in the order creation function, turning it into a 200-line monster that knows about emails, analytics, and inventory.

The next level is background job processing with BullMQ and Redis. Some event handlers should not run in the request cycle. Sending email takes 500ms to 2 seconds. Generating a PDF takes several seconds. AI classification takes 300ms to 3 seconds. The event handler pushes a job to the queue. A separate worker process picks it up with retry logic and exponential backoff. This is not a separate service. It is a separate entry point in the same codebase.

For critical events that must not be lost, we use the transactional outbox pattern. Instead of emitting events directly, write them to an outbox table in the same database transaction as the business operation. A separate poller reads unprocessed events and emits them. This guarantees that if the operation succeeds, the event eventually fires, even if the application crashes between the database write and the emission. The entire implementation is about 50 lines of code.

We do not use Kafka, RabbitMQ, or any external message broker for projects under 10,000 events per minute. Redis with BullMQ handles everything we need on SMB projects. We have never hit that threshold.

Critical distinction that people confuse: event-driven architecture is not event sourcing. You can emit events as side effects of state changes without making events the source of truth for state. The former is simple and broadly useful. The latter is complex and narrowly applicable.

The recommended progression: start with in-process events to decouple modules. Add BullMQ for background processing when operations should not block requests. Add the outbox pattern for critical events. Add a dedicated message broker only when volume outgrows BullMQ. Each step is natural evolution when the need arises, not an upfront architecture commitment.

About the Author

Fordel Studios

AI-native app development for startups and growing teams. 14+ years of experience shipping production software.

Want to discuss this further?

We love talking shop. If this article resonated, let's connect.

Start a Conversation

Ready to build
something real?

Tell us about your project. We'll give you honest feedback on scope, timeline, and whether we're the right fit.

Start a Conversation