Chain of Responsibility Pattern in C#

The Chain of Responsibility Pattern lets you pass a request along a chain of handlers until one of them deals with it. Each handler decides whether to process the request or pass it to the next link in the chain.

Real-Life Analogy: Leave Request Approval Chain

Imagine you're an employee requesting leave. First, you ask your team leader. If they approve, great. If not, it goes up to the department manager. Still no? It reaches HR or upper management.

The request goes through each level until someone handles it — or it's rejected. That's how the Chain of Responsibility works in code.

Employee leave request passed from team lead to manager to HR
Like a leave request climbing up the management chain, the Chain of Responsibility Pattern passes a request from one handler to the next.

Benefits of Chain of Responsibility

This pattern keeps your code clean by letting each class focus on just one type of responsibility. It also lets you add or remove handlers without changing the rest of the system.

It promotes flexibility and separation of concerns.

  • Reduces coupling between sender and receiver
  • Makes it easy to add or reorder handlers
  • Encapsulates each handler's responsibility

When Should You Use It?

Use this pattern when multiple objects could handle a request — but you don't want to hard-code the order or decision logic.

It works best when:

  • Requests may be handled by different objects
  • Handlers need to pass along requests they can't handle
  • You want to keep responsibilities loosely coupled

When Not to Use It: Avoid it if you know exactly which object should handle the request — no need to pass it down a chain.

What to Implement

To implement this pattern, you need:

A base handler class with a method to process the request and a reference to the next handler in the chain.

  • Handler Interface: Declares a method like Handle()
  • Concrete Handlers: Decide whether to process or pass along
  • Client: Sends the request to the first handler

How It Works in C#


// Request
public class LeaveRequest
{
    public int Days { get; }
    public LeaveRequest(int days) => Days = days;
}

// Handler Interface
public abstract class Approver
{
    protected Approver Next;

    public void SetNext(Approver next) => Next = next;

    public abstract void HandleRequest(LeaveRequest request);
}

// Concrete Handlers
public class TeamLead : Approver
{
    public override void HandleRequest(LeaveRequest request)
    {
        if (request.Days <= 2)
            Console.WriteLine(""TeamLead approved leave"");
        else
            Next?.HandleRequest(request);
    }
}

public class Manager : Approver
{
    public override void HandleRequest(LeaveRequest request)
    {
        if (request.Days <= 5)
            Console.WriteLine(""Manager approved leave"");
        else
            Next?.HandleRequest(request);
    }
}

public class HR : Approver
{
    public override void HandleRequest(LeaveRequest request)
    {
        Console.WriteLine(""HR approved leave"");
    }
}

Usage:


var teamLead = new TeamLead();
var manager = new Manager();
var hr = new HR();

teamLead.SetNext(manager);
manager.SetNext(hr);

var request = new LeaveRequest(4);
teamLead.HandleRequest(request);  // Manager approved leave

Final Thoughts

The Chain of Responsibility Pattern is perfect for systems where the handler isn't fixed. It gives you flexibility, avoids hard-coded logic, and lets responsibilities shift as needed.

Whether it's handling approvals, commands, or support tickets — this pattern lets your code flow through the right hands.