Design & Architecture

Reactive Systems

Advanced

A reactive system is built to stay responsive under load and failure: it is responsive, resilient, elastic, and message-driven, as set out in the Reactive Manifesto. Note the distinction — reactive systems are an architectural goal, while reactive programming (observables, streams) is one tool that can help reach it. Don't confuse adopting Rx with building a system that actually stays up.

The Reactive Manifesto names four properties that reinforce each other. Responsive: the system replies in a timely way, even under stress. Resilient: it stays responsive when parts fail, through isolation and recovery. Elastic: it scales out and in with load. Message-driven: components communicate via asynchronous messages, which is what enables the other three — loose coupling, isolation, and back-pressure.

This is closely related to Event-Driven Architecture and Asynchronous Messaging (message-driven is the foundation) and to Distributed Systems & Consistency and Real-time & WebSockets. The goal isn't to chase a buzzword or rewrite everything as streams — it's to make the system degrade gracefully instead of falling over, which for a service handling money and compliance matters a great deal.

Build for the four properties

Unbounded, blocking, fragile foreach (var item in hugeStream)
queue.Add(Process(item)); // no bound, no timeout
// consumer is slower than producer -> queue grows until OOM
// one slow downstream call blocks the whole thread

No back-pressure, no isolation, no timeout. Under load the queue grows without limit and a single slow dependency stalls everything.

Bounded, async, isolated var channel = Channel.CreateBounded(1000); // back-pressure
// producer awaits when full; consumer pulls at its own rate
await policy.ExecuteAsync(ct => ProcessAsync(item, ct));
// policy = timeout + retry + circuit breaker (bulkhead)

The bounded channel applies back-pressure so producers slow down instead of exhausting memory; the resilience policy isolates and times out failures.

Use reactive programming as a tool, not a goal

Self-review checklist

Why it matters: Systems rarely fail gently — under load or a failing dependency they tend to fall over all at once. Designing for the reactive properties (responsive, resilient, elastic, message-driven) means the system degrades gracefully instead: failures stay contained, load is absorbed with back-pressure, and capacity scales with demand. For a service handling money and compliance, staying responsive under stress is the difference between a slow moment and a regulatory incident — and you get there through good architecture, not merely a reactive library.