AlgoMaster Newsletter

AlgoMaster Newsletter

Share this post

AlgoMaster Newsletter
AlgoMaster Newsletter
Chain of Responsibility Design Pattern
Copy link
Facebook
Email
Notes
More
User's avatar
Discover more from AlgoMaster Newsletter
Master Coding and System Design Interviews. Level up your Software Engineering career. Subscribe and get a FREE System Design Interview Handbook in your inbox.
Over 96,000 subscribers
Already have an account? Sign in

Chain of Responsibility Design Pattern

Ashish Pratap Singh's avatar
Ashish Pratap Singh
Jun 15, 2025

Share this post

AlgoMaster Newsletter
AlgoMaster Newsletter
Chain of Responsibility Design Pattern
Copy link
Facebook
Email
Notes
More
Share

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:

  • A request must be handled by one of many possible handlers, and you don’t want the sender to be tightly coupled to any specific one.

  • You want to decouple request logic from the code that processes it.

  • You want to flexibly add, remove, or reorder handlers without changing the client code.

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.


The Problem: Handling HTTP Requests with Multiple Processing Steps

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.

Common Pre-processing Steps

  1. Authentication – Is the user properly authenticated (e.g., via token or session)?

  2. Authorization – Is the authenticated user allowed to perform this action?

  3. Rate Limiting – Has the user exceeded their allowed number of requests?

  4. Data Validation – Is the request payload well-formed and valid?

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:

Client Code Example

Why This Approach Breaks Down

While this approach may work for a small number of checks, it quickly becomes problematic:

1. Hard to Extend or Modify

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.

2. Poor Separation of Concerns

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.

3. No Reusability

You can’t easily reuse individual checks (e.g., rate limiting or authentication) in other parts of the system.

4. Inflexible Configuration

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.

What We Really Need

We need a way to:

  • Break each step into its own unit of responsibility

  • Let each step decide whether to handle, pass, or short-circuit the request

  • Allow new handlers to be added, removed, or reordered without touching the existing code

  • Keep our logic clean, testable, and extensible

This is where the Chain of Responsibility Pattern comes in.


The Chain of Responsibility Pattern

The Chain of Responsibility Pattern allows a request to be passed along a chain of handlers. Each handler in the chain can either:

  • Handle the request, or

  • Pass it to the next handler in the chain

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.

Class Diagram

1. Handler Interface (Abstract Base Class / Interface)

  • Declares a method like handle(request) for processing the request.

  • Holds a reference to the next handler in the chain via setNext(handler).

  • Defines the contract for passing the request down the chain.

2. ConcreteHandlers (e.g., AuthHandler, RateLimitHandler)

  • Implement the Handler interface.

  • Each handler decides if it will:

    • Handle the request (e.g., reject, log, transform), or

    • Pass the request along to the next handler in the chain.

3. Client

  • Builds and connects the chain of handlers using setNext().

  • Sends the request to the first handler in the chain.

  • Is unaware of which handler will ultimately process the request.


Implementing Chain of Responsibility

Let’s refactor our monolithic RequestHandler into a clean, extensible chain of modular handlers using the Chain of Responsibility Pattern.

1. Define the Common Handler Interface

Every handler will implement this interface. Each handler should:

  • Perform its specific check

  • Decide whether to stop the chain or pass the request to the next handler

2. Create the Abstract Base Handler (Optional but Recommended)

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.

3. Create Concrete Handlers

Each handler implements one responsibility. They extend BaseHandler, implement handle(Request), and determine whether to continue the chain or short-circuit it.

🔐 Authentication Handler

🔒 Authorization Handler

⏱️ Rate Limiting Handler

📦 Data Validation Handler

✅ Final Business Logic Handler

This is the last handler in the chain — it assumes the request has passed all previous checks.

4. Assemble the Chain in Client Code

Now that our handlers are modular, we can connect them in any order depending on the requirements.

Output

AuthHandler: ✅ Authenticated.
AuthorizationHandler: ✅ Authorized.
RateLimitHandler: ✅ Within rate limit.
ValidationHandler: ✅ Payload valid.
BusinessLogicHandler: 🚀 Processing request...

--- Trying an invalid request ---
AuthHandler: ❌ User not authenticated.

What We Achieved

  • Modularity: Each handler is isolated and easy to test

  • Loose Coupling: Handlers don’t need to know who comes next

  • Extensibility: Easily insert, remove, or reorder handlers

  • Clean Client Code: Only responsible for building the chain and sending the request

  • Open/Closed Compliant: You can add new functionality (e.g., LoggingHandler) without touching existing code


Additional Readings:

  • Refactoring Guru: Chain of Responsibility


Subscribe to AlgoMaster Newsletter

By Ashish Pratap Singh · Hundreds of paid subscribers
Master Coding and System Design Interviews. Level up your Software Engineering career. Subscribe and get a FREE System Design Interview Handbook in your inbox.

Share this post

AlgoMaster Newsletter
AlgoMaster Newsletter
Chain of Responsibility Design Pattern
Copy link
Facebook
Email
Notes
More
Share
© 2025 Ashish Pratap Singh
Privacy ∙ Terms ∙ Collection notice
Start writingGet the app
Substack is the home for great culture

Share

Copy link
Facebook
Email
Notes
More