Low Level Design

Chain of Responsibility — Pass Requests Through a Handler Chain

How the Chain of Responsibility pattern decouples request senders from receivers, letting multiple objects get a chance to handle a request without the sender knowing which one will.

August 26, 2026·9 min read

What is the Chain of Responsibility Pattern?#

The Chain of Responsibility is a behavioral pattern that lets you pass requests along a chain of handlers. Each handler decides either to process the request or to pass it to the next handler in the chain.

The sender is completely decoupled from the receiver — it just fires the request and the chain figures out who handles it.

You reach for Chain of Responsibility when:

  • Multiple objects may handle a request, but you don't know which one in advance
  • You want to issue requests to one of several objects without specifying the receiver explicitly
  • The set of handlers should be composable and dynamic
  • You want to process requests in a specific priority order

Real-World Analogy#

Think of a customer service call center. When you call with a problem:

  1. 🤖 Automated system handles simple queries (e.g., balance check)
  2. 👤 Junior rep handles basic issues (e.g., billing questions)
  3. 👩‍💼 Senior rep handles complex problems (e.g., service disputes)
  4. 🏢 Supervisor handles escalated complaints

Each level handles what it can and passes harder issues up. You (the caller) don't decide who handles your request — the chain does.


Class Diagram#


Violation Code — The Problem#

java
class SupportService {
    public void handleRequest(String issue, int priority) {
        if (priority == 1) {
            System.out.println("Level 1 handling: " + issue);
        } else if (priority == 2) {
            System.out.println("Level 2 handling: " + issue);
        } else if (priority == 3) {
            System.out.println("Level 3 handling: " + issue);
        } else {
            System.out.println("Manager handling: " + issue);
        }
        // Adding priority 5 means touching this method ❌
    }
}

Issues:

  1. No abstraction — no common Handler interface; all logic packed in one method
  2. Tight coupling — all support levels crammed into one class
  3. No delegation — each level isn't responsible for its own logic
  4. Violates OCP — adding a new support level means modifying existing code
  5. Untestable in isolation — can't test individual levels independently

Enhanced Code — Chain of Responsibility#

java
// Abstract handler — defines the chain link
public abstract class SupportHandler {
    private SupportHandler nextHandler;

    public SupportHandler setNext(SupportHandler next) {
        this.nextHandler = next;
        return next; // enables chaining: l1.setNext(l2).setNext(l3)
    }

    public void handleRequest(String issue, int priority) {
        if (nextHandler != null) {
            nextHandler.handleRequest(issue, priority);
        } else {
            System.out.println("No handler found for priority " + priority);
        }
    }
}

// Concrete handlers
public class Level1Support extends SupportHandler {
    @Override
    public void handleRequest(String issue, int priority) {
        if (priority == 1) {
            System.out.println("Level 1: Handling '" + issue + "'");
        } else {
            System.out.println("Level 1: Escalating...");
            super.handleRequest(issue, priority);
        }
    }
}

public class Level2Support extends SupportHandler {
    @Override
    public void handleRequest(String issue, int priority) {
        if (priority == 2) {
            System.out.println("Level 2: Handling '" + issue + "'");
        } else {
            System.out.println("Level 2: Escalating...");
            super.handleRequest(issue, priority);
        }
    }
}

public class Level3Support extends SupportHandler {
    @Override
    public void handleRequest(String issue, int priority) {
        if (priority == 3) {
            System.out.println("Level 3: Handling '" + issue + "'");
        } else {
            System.out.println("Level 3: Escalating...");
            super.handleRequest(issue, priority);
        }
    }
}

public class ManagerSupport extends SupportHandler {
    @Override
    public void handleRequest(String issue, int priority) {
        System.out.println("Manager: Taking ownership of '" + issue + "'");
    }
}

// Build the chain
public class Main {
    public static void main(String[] args) {
        SupportHandler l1 = new Level1Support();
        SupportHandler l2 = new Level2Support();
        SupportHandler l3 = new Level3Support();
        SupportHandler manager = new ManagerSupport();

        l1.setNext(l2).setNext(l3).setNext(manager); // chain it

        l1.handleRequest("Password reset", 1);    // Level 1 handles
        l1.handleRequest("Billing dispute", 2);   // Level 2 handles
        l1.handleRequest("Account hacked", 4);    // Manager handles
    }
}

Use a Builder or Factory to construct the chain — this keeps the chain wiring out of business logic.


Common LLD Problems Using Chain of Responsibility#

1. Logging System#

  • Handlers: DebugLogger, InfoLogger, WarningLogger, ErrorLogger
  • Context: Messages pass through logger levels based on severity.

2. HTTP Middleware Pipeline (Web Frameworks)#

  • Handlers: AuthMiddleware, RateLimiter, RequestLogger, RequestValidator
  • Context: Each middleware processes the request and passes it on (or rejects it).

3. Expense Approval Workflow#

  • Handlers: ManagerApproval, DirectorApproval, CFOApproval
  • Context: Expense requests escalate until approved at the appropriate authority level.

4. Event Handling System#

  • Handlers: MouseEventHandler, KeyboardEventHandler, TouchEventHandler
  • Context: Events bubble up through the chain until a handler processes them.

5. Access Control / Authorization#

  • Handlers: RoleCheckHandler, PermissionHandler, OwnershipHandler
  • Context: Multiple access rules are checked in sequence before granting access.

6. Form Validation Pipeline#

  • Handlers: EmptyFieldValidator, EmailFormatValidator, PasswordStrengthValidator
  • Context: Each validator passes or blocks the form submission.

7. Spam Filter / Email Classifier#

  • Handlers: KeywordFilter, SenderReputationFilter, DomainFilter
  • Context: Emails pass through multiple filters to determine legitimacy.

8. Technical Support Escalation#

  • Handlers: Tier1Support, Tier2Support, Tier3Support, Engineering
  • Context: Issues are passed up the chain until they land with someone who can resolve them.

Key Design Tips#

  • Use a Builder or Factory to wire the chain — keeps chain construction separate from handler logic
  • Decide on fallback — does an unhandled request fail silently or throw an exception?
  • Each handler should do one thing — SRP applies per link in the chain
  • Chain order matters — handlers are tried in the order they are linked

ReferencesLinks
Article ReferenceRefactoring Guru — Chain of Responsibility