Back to blog
Coding

Event-Driven Microservices in 2026: Sagas, Outboxes, and the Modular Monolith Comeback

6 min read

The microservices conversation in 2026 looks different from 2018. Back then the question was "how do we split this monolith." Now it's "which parts should actually be split, and which should stay together as a modular monolith with an internal event bus." Teams that spent years decomposing applications into dozens of services are, in meaningful numbers, consolidating back — not into the old tangled monolith, but into something more deliberate (Enqcode). Event-driven architecture (EDA) sits underneath both trends, because it's not really a competitor to microservices or monoliths — it's a communication pattern you can apply inside either.

What actually broke with naive microservices

The core problem is mechanical: every function call that used to be in-process becomes a network call once you split a service boundary. That single change means you're no longer just writing business logic — you're handling partial failures, retry storms, circuit breakers, and eventual consistency as first-class concerns (Java Code Geeks). Rising cloud costs, added latency, operational overhead, and cross-team coordination friction are the concrete reasons companies are rethinking the reflexive "microservices by default" posture (Java Code Geeks).

Event-driven systems have their own distinct failure mode worth naming directly: instead of one thing crashing loudly and visibly, small inconsistencies creep in quietly across services, which makes them some of the hardest bugs to trace back to a root cause (Java Code Geeks).

Warning

Distributed systems are inherently more complex than monoliths, and without mature DevOps practices, monitoring, and governance, microservices tend to produce operational fragility rather than agility (Java Code Geeks).

Choreography vs. orchestration: the central design choice

Once you're committed to event-driven communication, the next decision is how services coordinate multi-step workflows that span service boundaries. Two patterns dominate:

  • Choreography — no central coordinator. Each service listens for events and emits the next event based on its own local logic. It's decentralized and fits event-driven principles cleanly, but becomes hard to understand and debug as the number of participating services grows (Encore).
  • Orchestration — a central coordinator issues commands to each service and reacts to their results, maintaining the saga's state explicitly. This gives you visibility and easier testing, at the cost of a potential single point of failure and tighter coupling to the orchestrator (Encore).

The 2026 consensus has settled into a pragmatic middle: choreography for simple, naturally decoupled flows; orchestration for anything where sequencing, visibility, and compensation logic actually matter; and hybrid designs that use both within the same system depending on the subdomain (Encore).

Choreography Orchestration
Coordination Decentralized, event-driven Centralized coordinator/orchestrator
Visibility into workflow state Low — must be reconstructed from event history High — orchestrator holds explicit state
Debuggability Harder as service count grows Easier — single place to trace flow
Coupling Loose between services, but implicit Explicit coupling to orchestrator
Best fit Simple, few-step flows Complex, multi-step flows needing compensation

The saga pattern and compensating transactions

Distributed transactions don't have ACID guarantees across service boundaries, so the saga pattern trades strict consistency for eventual consistency with explicit recovery logic: a sequence of local transactions, each paired with a compensating transaction that semantically undoes it if a later step fails (Microsoft Learn). If step 3 of a 5-step order-fulfillment saga fails, the system runs compensations for steps 2 and 1 rather than attempting a cross-service rollback that doesn't actually exist at the infrastructure level.

Orchestrated saga (order placement):
1. OrderService: create order (pending)
2. PaymentService: charge card       -- fails
   -> compensate: OrderService: cancel order
   -> compensate: InventoryService: release reserved stock

The outbox pattern: fixing the dual-write problem

A subtle but extremely common bug in event-driven systems: a service updates its database, then separately publishes an event about that update. Those two operations aren't atomic — if the process crashes between them, or the message broker is briefly unavailable, the database and the event stream disagree about what happened (Encore). The outbox pattern fixes this by writing the event into an "outbox" table inside the exact same database transaction as the state change, then using a separate relay process (often CDC-based) to publish outbox rows to the event stream asynchronously (Encore). This guarantees the event is eventually published if and only if the state change actually committed.

The modular monolith comeback

Perhaps the most notable 2026 trend is what looks, on the surface, like a retreat: teams that broke applications into dozens of microservices are reassembling them — not into the old tightly-coupled monolith, but into a modular monolith with clean internal module boundaries and, often, an internal event bus replacing what used to be inter-service HTTP calls (Enqcode). This isn't a rejection of event-driven design — it's a rejection of paying network-call latency and operational overhead for boundaries that didn't need to be physical processes in the first place.

The practical framing that's emerged: event-driven is a pattern you apply inside whichever structural choice fits your team and scale — a modular monolith can use an internal event bus for decoupling without the operational cost of a message broker and distributed tracing across dozens of deployables (Medium: Tara Prasad Routray).

Decision framework for 2026

Rather than defaulting to microservices because that's the industry-standard conference-talk architecture, the working framework teams are converging on:

  1. Start modular, not distributed. Build clean module boundaries inside a single deployable first. Split into a separate service only when you have a concrete, measured reason — independent scaling needs, a genuinely separate team owning it, or a hard deployment-cadence mismatch.
  2. Pick choreography or orchestration per-workflow, not globally. A simple two-step flow doesn't need a saga orchestrator; a checkout-to-fulfillment pipeline with five failure-prone steps probably does.
  3. Adopt the outbox pattern anywhere you publish events tied to a state change. It's cheap insurance against silent data/event divergence that is otherwise very hard to debug after the fact.
  4. Invest in observability before you split further. Distributed tracing, structured event logging, and correlation IDs across service boundaries are prerequisites for event-driven systems to be debuggable at all — not optional polish added later.

Tip

The actionable takeaway: don't ask "microservices or monolith" as a binary architecture decision anymore. Ask "which specific boundaries need to be physically separate services," apply event-driven communication (choreography or orchestration, chosen per workflow) at those boundaries, and default everything else to a well-modularized single deployable with an internal event bus.


Sources: Encore: Event-Driven Architecture in 2026, Microsoft Learn: Saga Design Pattern, Java Code Geeks: Microservices vs Monoliths in 2026, Enqcode: Rethinking Microservices in 2026, Medium: Monolith vs Microservices vs Event-Driven Architecture

Get new posts as they publish

No spam — just the next post, straight to your inbox.

Keep reading

Discussion