Chain of responsibility pattern
Chain of responsibility passes a request along a handler pipeline until one processes it. Decouple senders from receivers with composable, reorderable handler chains.
Introduction
Chain of Responsibility passes a request through a sequence of handlers. Each handler owns one decision or processing step and can either handle the request, stop propagation, or delegate to the next handler.
TL;DR / mental model: Give each step one responsibility, wire the steps into a chain, and let each handler choose between short-circuiting and forwarding.
Problem and Context
You're building an HTTP request pipeline. Every incoming request must be authenticated, rate-limited, logged, and validated before reaching the business logic. Without a pattern, you nest these checks inside one method.
Four checks today, eight next quarter. Each new concern means reopening this class. Reordering is a risky code change. Disabling one check for testing requires commenting out code. You cannot compose different pipelines for different endpoints without duplicating the entire method.
When It Helps
Use Chain of Responsibility when processing steps are independent, optional, reorderable, and may stop the request. For a fixed sequence where every step always runs, direct method calls or another pipeline design may be clearer.
Participants and Structure
Handler defines two methods: setNext() for wiring the chain, and handle() for processing or forwarding. BaseHandler implements the default forwarding logic so concrete handlers only override when they need to act. Each concrete handler (AuthHandler, RateLimitHandler, LoggingHandler, ValidationHandler) decides whether to stop the chain (return an error) or pass through to the next handler.
The key insight: each handler knows only about the next handler, never the full chain. The client builds the chain once and sends requests to the first handler.
Idiomatic Example and Implementation Notes
This pattern is useful for a configurable pipeline where handlers can be added, removed, or reordered without changing existing handler classes. The base handler class provides the forwarding behavior so each concrete handler can focus on its own concern.
Pass-through vs short-circuit
A handler that logs the request should always call next. A handler that rejects unauthorized requests should return immediately without calling next. This distinction (pass-through vs short-circuit) is what makes the chain powerful.
Continue Reading with Premium
Unlock this article and every other in-depth system design guide on the platform with SDEpedia Premium.
Related Articles
The command pattern turns requests into objects so you can queue, log, and undo operations. Decouple sender from receiver by encapsulating actions.
The decorator pattern wraps objects to add behavior at runtime without modifying the original class. Stack decorators like layers, each adding one responsibility while keeping the same interface.
The strategy pattern extracts a family of algorithms behind an interface so the client can swap behaviors at runtime without touching the context class.