The Chain of Responsibility Design Pattern is a behavioral pattern that lets you pass requests along a chain of handlers, allowing each handler to decide whether to process the request or pass it to the next handler in the chain.
It’s particularly useful in situations where:
When dealing with conditional request handling, developers often resort to long chains of if-else
or switch
statements to determine how a request should be processed. For example, a logging system might write to the console, file, or remote server depending on configuration, or an HTTP request might need to go through validation, authentication, and rate-limiting steps.
But as the number of conditions grows, this approach becomes hard to scale, violates the Open/Closed Principle, and turns your logic into a tightly coupled, brittle monolith.
The Chain of Responsibility Pattern solves this by turning individual processing steps into standalone classes, each responsible for one specific concern. These handlers are linked together to form a chain, and the request flows through the chain until it is handled (or dropped).
Let’s walk through a real-world example to see how we can apply the Chain of Responsibility Pattern to build a clean, modular, and extensible pipeline for request processing.
Imagine you’re building a backend server that processes incoming HTTP requests for a web application or RESTful API.
Here’s what a request might look like:
Each incoming request must go through a sequence of processing steps before it reaches the core business logic.
Only after all these checks pass should the request reach the actual business logic (e.g., creating a resource, returning data, or updating a record).
A typical first attempt might look like this: implement all logic inside a single class using a long chain of if-else
or method calls.
Here’s a simplified version:
While this approach may work for a small number of checks, it quickly becomes problematic:
If you want to change the order of checks or add a new one (e.g., logging, metrics, caching), you must modify the existing handler — violating the Open/Closed Principle.
All validation and control logic is tightly coupled inside a single method. This violates the Single Responsibility Principle and makes the code harder to test and maintain.
You can’t easily reuse individual checks (e.g., rate limiting or authentication) in other parts of the system.
Want to skip authorization for public APIs? Want to make validation optional in dev mode? You’ll have to write more if
conditions and bloat the handler.
We need a way to:
This is where the Chain of Responsibility Pattern comes in.
The Chain of Responsibility Pattern allows a request to be passed along a chain of handlers.
Each handler in the chain can either:
This pattern decouples the sender of the request from the receiver(s), giving you the flexibility to compose chains dynamically, reuse logic, and avoid long, rigid conditional blocks.
handle(request)
for processing the request.setNext(handler)
.AuthHandler
, RateLimitHandler
)Implement the Handler
interface.
Each handler decides if it will:
setNext()
.Let’s refactor our monolithic RequestHandler
into a clean, extensible chain of modular handlers using the Chain of Responsibility Pattern.
Every handler will implement this interface. Each handler should:
To avoid duplicating the setNext()
and forwarding logic in every handler, we define an abstract base class with reusable functionality.
Now every concrete handler can focus solely on its logic and delegate to
forward(request)
when needed.
Each handler implements one responsibility. They extend BaseHandler
, implement handle(Request)
, and determine whether to continue the chain or short-circuit it.
This is the last handler in the chain — it assumes the request has passed all previous checks.
Now that our handlers are modular, we can connect them in any order depending on the requirements.