November 4, 2026
App Development

Event-Driven Architecture in 2027: Kafka, NATS, Pulsar, and the Outbox Pattern

Event-driven architecture in 2027 means picking the right broker for the job — Kafka for durable, replayable log-based streaming, NATS for lightweight low-latency messaging, Pulsar for multi-tenant geo-replication — then solving the dual-write problem with the transactional outbox pattern and deciding deliberately between event sourcing and CQRS rather than adopting both by default.
BrokerCore strengthWhere it strugglesTypical fit
Apache KafkaDurable, replayable commit log; huge ecosystem (Kafka Connect, ksqlDB, Schema Registry); proven at massive scaleOperationally heavy — needs real capacity planning (JVM tuning, partition strategy, broker sizing); higher baseline latency than NATSEvent streaming platforms, audit-grade event logs, systems needing replay and long retention
NATS (JetStream)Extremely lightweight, low-latency, simple to operate; runs well at the edge and in resource-constrained environmentsSmaller ecosystem than Kafka; JetStream persistence is younger and less battle-tested at Kafka’s scaleMicroservice-to-microservice messaging, IoT/edge fan-out, request-reply patterns needing sub-millisecond latency
Apache PulsarSeparates compute (brokers) from storage (BookKeeper), enabling independent scaling and strong multi-tenancy and geo-replicationSteeper operational learning curve; smaller hiring pool than Kafka; more moving parts (ZooKeeper/BookKeeper coordination)Multi-tenant SaaS platforms, cross-region replication, mixed streaming + queuing workloads on one system

This guide covers event-driven architecture (EDA) as a backend systems design discipline: choosing a broker, avoiding the classic dual-write bug with the transactional outbox pattern, deciding between event sourcing and CQRS, managing schema evolution as services change independently, and being honest about when the operational overhead of EDA is worth paying versus when a simpler request-response API is the better call.

What “Event-Driven” Actually Buys You in 2027

An event-driven architecture decouples services in time: a producer emits a fact (“order placed,” “payment captured,” “inventory reserved”) without knowing or caring which consumers will react to it, and consumers subscribe to the events they care about and process them independently, often asynchronously. The payoff is that services can be deployed, scaled, and even fail independently — a downstream email service being down doesn’t block checkout, it just means the “order confirmed” email is delayed until the consumer catches up. That resilience and independent scalability is the entire argument for EDA, and it’s also exactly what makes it harder to reason about: you trade the simplicity of a synchronous call stack for eventual consistency, message ordering questions, and a genuinely harder debugging story when something goes wrong three services downstream from the original event.

The pattern has matured well past “just put a queue between two services,” and it’s part of a broader shift in how backend systems get architected — a shift covered from the AI-infrastructure angle in our piece on re-architecting IT backends. Modern event-driven systems typically combine a durable broker, a schema registry to keep producers and consumers from breaking each other, an outbox pattern to guarantee events are never lost on write, and — for a subset of services where audit history has real business value — event sourcing as the system of record instead of a traditional row-based database.

Kafka vs NATS vs Pulsar: Choosing a Broker

The three brokers most commonly evaluated for new event-driven systems in 2027 solve the same broad problem with meaningfully different architectures. Kafka’s core abstraction is a durable, partitioned, replayable commit log — consumers track their own offset and can rewind to reprocess history, which is what makes Kafka the default choice for event streaming platforms, analytics pipelines, and any system where “replay the last 30 days of events” needs to be a real operational capability. NATS, by contrast, was built around simplicity and speed: a lean core protocol with JetStream added on top for persistence, and it tends to win in latency-sensitive microservice communication and edge/IoT scenarios where Kafka’s operational footprint (JVM, ZooKeeper or KRaft, careful partition planning) is disproportionate to the workload. Pulsar sits architecturally between the two — it splits the serving layer (brokers) from the storage layer (Apache BookKeeper), which lets you scale compute and storage independently and gives Pulsar strong native multi-tenancy and geo-replication, at the cost of more moving infrastructure parts to operate.

A pragmatic decision rule: reach for Kafka when replay, retention, and a mature ecosystem (Kafka Connect source/sink connectors, ksqlDB, ecosystem tooling) matter most; reach for NATS when you want the lightest possible operational footprint and your latency requirements are aggressive; reach for Pulsar when multi-tenancy or active-active geo-replication across regions is a hard requirement from day one, since retrofitting that onto Kafka later is painful. None of the three is strictly “better” — they encode different bets about what will be operationally expensive for your team.

The Transactional Outbox Pattern: Solving the Dual-Write Problem

The most common bug in early event-driven systems isn’t a broker misconfiguration — it’s the dual-write problem. A service updates its database and then publishes an event as two separate operations; if the process crashes between them, or the broker call fails after the database commit succeeds, you get a database that says “order placed” with no corresponding event ever published, silently breaking every downstream consumer. The transactional outbox pattern solves this by writing the event into an “outbox” table in the same local database transaction as the business write, so both succeed or fail atomically. A separate relay process — often using change-data-capture tools like Debezium reading the database’s write-ahead log — then reads unpublished rows from the outbox and forwards them to the broker, marking them sent once delivered.

  • Step 1 — Same-transaction writeThe service writes its business row (e.g., an orders table update) and an outbox row describing the event in one local database transaction, so a crash mid-write leaves no orphaned state.
  • Step 2 — Change-data-capture tailA CDC tool like Debezium tails the database’s transaction log and picks up new outbox rows without the application needing to poll or manage a second write path.
  • Step 3 — Publish to the brokerThe relay forwards the outbox event to Kafka, NATS, or Pulsar, using the outbox row’s own ID as an idempotency key so retries after a crash never produce duplicate downstream effects.
  • Step 4 — Mark delivered, prune laterOnce the broker acknowledges receipt, the relay marks the outbox row processed; a background job periodically archives or deletes old rows so the table doesn’t grow unbounded.
  • Step 5 — Consumers de-duplicateDownstream consumers treat delivery as at-least-once and de-duplicate on the event’s idempotency key, since the outbox pattern guarantees an event is published but not that it’s published exactly once.

Figure 1 — Transactional outbox pattern with CDC relay

A diagram shows a service box on the left containing “Business table” and “Outbox table” side by side, connected by a dashed line labeled “single local transaction.” An arrow from the Outbox table leads to a “CDC relay (e.g., Debezium)” box, which reads the database’s write-ahead log rather than querying the outbox table directly. From the relay, an arrow labeled “at-least-once publish” leads to a central “Broker (Kafka / NATS / Pulsar)” icon, which fans out to three consumer boxes on the right, each labeled “de-duplicates on event ID.”

The outbox table and the CDC relay are what make the write atomic from the application’s point of view — the broker never needs to participate in the local database transaction.

Event Sourcing vs CQRS: Two Different Decisions

Event sourcing and CQRS get bundled together constantly, but they answer different questions and you can adopt either one without the other. Event sourcing changes what your system of record actually is: instead of storing current state (an “orders” row that gets overwritten on each update), you store the full sequence of immutable events that produced that state (“OrderPlaced,” “ItemAdded,” “OrderShipped”) and derive current state by replaying them. This buys you a complete audit trail for free and the ability to rebuild any past state, at the cost of needing to think in events for every state change and building “projections” to answer simple queries efficiently. CQRS (Command Query Responsibility Segregation) is a separate decision: it splits the write model (commands that change state) from the read model (queries that retrieve it), letting each be scaled, indexed, and optimized independently — which matters when your read patterns and write patterns have very different shapes (e.g., high-write order intake feeding a handful of heavily-read reporting dashboards).

They pair naturally because event sourcing needs to materialize read-optimized projections from its event log anyway, and CQRS needs a reliable way to keep the read model in sync with writes — which an event stream provides cleanly. But plenty of systems use CQRS with a conventional database on both sides (no event sourcing at all), and plenty of systems use event sourcing with a single unified read/write model for smaller-scale use cases. The mistake is treating “we’re doing EDA” as an automatic signal to bolt on both patterns everywhere; each one earns its complexity in specific parts of a system, not the whole thing.

QuestionEvent sourcingCQRS
What does it change?Your system of record — events instead of current-state rowsHow reads and writes are modeled and scaled — can sit on top of any storage
What do you get?Full audit history, time-travel/rebuild, natural fit with event streamsIndependently scaled and optimized read/write paths
What’s the cost?Every state change must be an event; projections needed for queriesEventual consistency between write and read models; more moving parts
Can you adopt one without the other?Yes — event sourcing with a single combined read/write model works for smaller systemsYes — CQRS with plain CRUD on both sides, no event log at all

Schema Evolution: Keeping Producers and Consumers From Breaking Each Other

Once more than one team owns services on either side of an event stream, schema drift becomes the practical failure mode that actually causes outages — a producer adds a field, renames one, or changes a type, and a consumer written against the old shape starts throwing errors or silently misreading data. Schema registries (Confluent Schema Registry being the reference implementation for Kafka, with Avro, Protobuf, and JSON Schema all commonly supported) solve this by making every producer register its schema centrally and enforcing a compatibility mode — backward, forward, or full — before a new schema version is allowed to ship. Backward compatibility means new consumers can read old data; forward compatibility means old consumers can read new data; full compatibility requires both, which is the safest default for any event that multiple independent teams consume.

The practical discipline that keeps this manageable: add fields with defaults rather than removing or renaming existing ones, never reuse a field’s old name for a different meaning, and treat a schema registry’s compatibility check as a CI gate the same way you’d gate a database migration — a schema change that breaks compatibility should fail a build, not surface as a production incident three weeks later when a consumer team finally notices dropped messages.

When Event-Driven Architecture Is Worth the Complexity — and When It Isn’t

EDA is not free, and the honest failure mode of most over-engineered systems is adopting it where a synchronous REST API would have been simpler, faster to build, and easier to debug. The operational cost is real: you need broker infrastructure (or a managed equivalent), monitoring for consumer lag, a plan for poison messages and dead-letter queues, and engineers who can reason about eventual consistency instead of a linear call stack. That cost is worth paying when you have genuinely independent services that need to scale separately, when a single business event fans out to many consumers that shouldn’t block each other, or when audit history and replay are real product requirements rather than nice-to-haves.

It’s usually not worth it for a small number of tightly coupled services, a system where strong immediate consistency is a hard requirement (financial balance checks, inventory holds at checkout), or an early-stage product where the team doesn’t yet know its domain boundaries well enough to define stable event contracts — publishing an event schema too early, before the domain has settled, tends to produce exactly the kind of brittle, hard-to-evolve contract that schema registries are built to prevent, just discovered the hard way.

Common mistakes

  • Publishing events directly after a database write with no outbox pattern, silently losing events on process crashes or broker hiccups.
  • Adopting event sourcing and CQRS together by default across an entire system instead of applying each where its specific trade-off actually pays off.
  • Treating a schema registry as documentation instead of a CI-enforced compatibility gate that blocks breaking changes before they ship.
  • Choosing Kafka because it’s the popular default, then paying its full operational cost for a workload NATS could have served far more simply.
  • Assuming “at-least-once” delivery means “exactly-once” and skipping idempotency keys on the consumer side, leading to duplicate side effects on retry.

In practice

Teams that run EDA successfully at scale tend to treat the broker choice, the outbox implementation, and the schema registry as infrastructure decisions made once and enforced everywhere — not per-service choices left to individual teams. They wire schema-compatibility checks into the same CI pipeline that gates deploys, the same discipline covered in our API launch day guide, and they provision the underlying data stores — often a mix of relational databases for the outbox and a purpose-built store for projections — using the same evaluation framework laid out in our guide to choosing between RDS, DynamoDB, and Cloud SQL. Consumer lag and dead-letter queue depth get the same monitoring rigor as request latency, because a silently backed-up consumer is functionally the same failure as a slow API endpoint — it just takes longer for anyone to notice.

Glossary

Event-driven architecture (EDA)
A design style where services communicate by publishing and subscribing to events rather than calling each other directly and synchronously.
Transactional outbox pattern
A technique that writes an event to a local database table in the same transaction as a business write, then relays it to a broker asynchronously, avoiding the dual-write problem.
Event sourcing
Storing a sequence of immutable events as the system of record instead of overwriting current-state rows, deriving current state by replaying events.
CQRS
Command Query Responsibility Segregation — splitting the write (command) model from the read (query) model so each can be scaled and optimized independently.
Schema registry
A centralized service (e.g., Confluent Schema Registry) that stores event schemas and enforces compatibility rules before a producer can publish a new schema version.
Consumer lag
The gap between the latest event published to a broker partition/topic and the offset a given consumer has actually processed.
Dead-letter queue
A holding topic/queue for events a consumer repeatedly fails to process, so poison messages don’t block the rest of the stream.
Idempotency key
A unique identifier attached to an event or request that lets a consumer safely ignore duplicate deliveries without producing duplicate side effects.

Key Takeaways

  • Kafka, NATS, and Pulsar solve overlapping problems with different trade-offs — replay and ecosystem depth, lightweight low latency, and multi-tenant geo-replication, respectively.
  • The transactional outbox pattern is the standard fix for the dual-write problem: write the event and the business change in one local transaction.
  • Event sourcing and CQRS are separable decisions; adopt each where its specific trade-off earns its complexity, not as a package deal.
  • Schema registries with enforced backward/forward compatibility are what keep independently deployed producers and consumers from breaking each other.
  • At-least-once delivery is the realistic default; idempotency keys on the consumer side are non-negotiable, not optional hardening.
  • EDA is worth its operational cost for independently scaling services, high fan-out, and real audit/replay needs — not for small, tightly coupled systems needing strong consistency.
  • Broker choice, outbox implementation, and schema governance work best as centralized platform decisions, not per-team choices.

FAQs

Should a new project default to Kafka for event-driven architecture?

Not automatically. Kafka is the strongest choice when you need replay, long retention, and its mature connector ecosystem, but its operational overhead is real. If your workload is lighter-weight microservice messaging or edge/IoT fan-out with tight latency needs, NATS JetStream is often a simpler, cheaper fit.

What problem does the transactional outbox pattern actually solve?

It solves the dual-write problem: without it, a service’s database write and its event publish are two separate operations, and a crash between them silently loses the event. The outbox pattern writes the event in the same local transaction as the business change, guaranteeing they succeed or fail together.

Do I need event sourcing to use CQRS, or vice versa?

No. They’re separable. CQRS can run on top of a conventional database with no event log at all, and event sourcing can back a single combined read/write model without a separate CQRS layer. They pair well together but each earns its own complexity independently.

How does a schema registry prevent breaking changes?

It enforces a compatibility mode — backward, forward, or full — before allowing a new schema version to be registered. A change that would break existing consumers (removing a field without a default, changing a type) fails the compatibility check and can’t be published, catching the problem before it reaches production.

What does Apache Pulsar do differently from Kafka?

Pulsar separates the serving layer (brokers) from the storage layer (Apache BookKeeper), letting compute and storage scale independently. This gives Pulsar strong native multi-tenancy and geo-replication support, though it introduces more operational moving parts than Kafka’s more unified architecture.

When is event-driven architecture not worth adopting?

When you have a small number of tightly coupled services, when a flow needs strong immediate consistency (like a financial balance check), or when your team hasn’t yet stabilized its domain boundaries enough to define durable event contracts. In those cases, a synchronous API is simpler to build, run, and debug.

What does “at-least-once” delivery mean in practice?

It means a consumer might receive the same event more than once, typically after a retry following a delivery failure or acknowledgment timeout. Consumers must be designed to de-duplicate using an idempotency key so processing the same event twice doesn’t produce duplicate side effects like double-charging a customer.

How do dead-letter queues fit into an event-driven system?

A dead-letter queue holds events that a consumer has repeatedly failed to process, so a single malformed or “poison” message doesn’t block the rest of the stream behind it. Teams monitor dead-letter queue depth the same way they monitor error rates, and build tooling to inspect and replay those events after a fix ships.

Can event-driven architecture and serverless functions work together?

Yes — many teams trigger serverless functions directly from broker events (e.g., a Lambda or Azure Function consuming from a Kafka topic or queue) to process events without managing long-running consumer infrastructure, an approach also covered in our guide to getting started with serverless functions. This works well for bursty, stateless event processing but needs careful handling of concurrency limits and retry/backoff behavior.

References

  • “Pattern: Transactional outbox,” microservices.io, https://microservices.io/patterns/data/transactional-outbox.html
  • “Event Sourcing pattern,” Azure Architecture Center, Microsoft Learn, https://learn.microsoft.com/en-us/azure/architecture/patterns/event-sourcing
  • “CQRS pattern,” Azure Architecture Center, Microsoft Learn, https://learn.microsoft.com/en-us/azure/architecture/patterns/cqrs
  • “Schema Evolution and Compatibility,” Confluent Documentation, https://docs.confluent.io/platform/current/schema-registry/fundamentals/schema-evolution.html
  • “Compare NATS,” NATS Docs, NATS.io, https://docs.nats.io/nats-concepts/overview/compare-nats
  • “Apache Pulsar Documentation,” Apache Software Foundation, https://pulsar.apache.org/docs/
  • “Apache Kafka Documentation,” Apache Software Foundation, https://kafka.apache.org/documentation/
    Avatar photo
    Sophie Williams first earned a First-Class Honours degree in Electrical Engineering from the University of Manchester, then a Master's degree in Artificial Intelligence from the Massachusetts Institute of Technology (MIT). Over the past ten years, Sophie has become quite skilled at the nexus of artificial intelligence research and practical application. Starting her career in a leading Boston artificial intelligence lab, she helped to develop projects including natural language processing and computer vision.From research to business, Sophie has worked with several tech behemoths and creative startups, leading AI-driven product development teams targeted on creating intelligent solutions that improve user experience and business outcomes. Emphasizing openness, fairness, and inclusiveness, her passion is in looking at how artificial intelligence might be ethically included into shared technologies.Regular tech writer and speaker Sophie is quite adept in distilling challenging AI concepts for application. She routinely publishes whitepapers, in-depth pieces for well-known technology conferences and publications all around, opinion pieces on artificial intelligence developments, ethical tech, and future trends. Sophie is also committed to supporting diversity in tech by means of mentoring programs and speaking events meant to inspire the next generation of female engineers.Apart from her job, Sophie enjoys rock climbing, working on creative coding projects, and touring tech hotspots all around.

      Leave a Reply

      Your email address will not be published. Required fields are marked *