Anti-patterns in event-driven architecture

Event‑driven architecture and event sourcing attract both praise and frustration, with engineers sharing long‑running successes in domains like banking, insurance, analytics, and games alongside stories of costly failures and unmaintainable systems. Key themes include when asynchronous, decoupled designs actually fit (cross‑domain integration, auditability, high‑latency side work) versus when simple synchronous CRUD or command‑driven models are cheaper and clearer, as well as recurring pain points around debugging, schema evolution, ordering, Kafka complexity, and overuse of microservices. Many conclude that events are a powerful but unforgiving tool that demand strong tooling, observability, and careful scoping rather than being adopted as a default architecture.

Scope of experiences

  • Many commenters have long-running event-driven systems; others report outright disasters.
  • Domains with notable success: insurance and regulated industries (audit trails), banking and trading, manufacturing, public transport ticketing, some embedded systems, and especially games.
  • Several note heavy internal use of event concepts inside databases, VCS, and JS/GUI apps, even when the “architecture” isn’t branded as EDA.

Where event-driven shines

  • Integration across product suites and heterogeneous systems.
  • Moving slow or unreliable operations off the user’s critical path (emails, settlements, notifications, config distribution).
  • Domains that naturally produce “facts” over time and need full history, replay, or audit (ledgers, ticketing, some analytics).
  • Game dev and complex async systems where evented state machines are a natural fit.
  • Command-driven systems using messages as async instructions, not as primary state, are seen as a pragmatic middle ground.

Common anti-patterns and failures

  • Overuse: applying EDA everywhere, including simple request/response flows that would be clearer as direct function calls or CRUD APIs.
  • “Vanity” or resume-driven architectures: complex Kafka/event-sourcing stacks where a monolith would suffice.
  • Modeling queries and tightly coupled workflows as events; using commands with multiple consumers; building distributed monoliths.
  • Poor contracts: inconsistent wire formats, weak schema/versioning discipline, and non-deterministic consumers make replay and evolution fragile.
  • Operational pain: Kafka and cloud queues introduce infra overhead, difficult local setups, laggy consumers, ordering headaches, and costly observability.
  • Debuggability: hard to trace who consumes what, why something didn’t happen, or where a bug arose; no easy “find usages” for events.

Design guidance and patterns

  • Start from business/SLA: “can they wait?” If not, prefer synchronous calls.
  • Treat events as immutable facts; commands as single-consumer imperatives; avoid events-for-queries.
  • Favor entity-centric modeling (“nouns over verbs”), with commands and events changing entity state.
  • Embrace additive-only changes, idempotent consumers, deterministic projectors, DLQs, and replay strategies; accept at-least-once semantics where appropriate.
  • Invest heavily in telemetry (trace IDs, distributed tracing, shared logging infrastructure).

Meta-consensus

  • EDA is powerful but unforgiving: it magnifies both good and bad design.
  • Many argue microservices plus EDA are over-applied; a well-structured monolith with selective async pieces often wins on cost, simplicity, and time-to-market.