Chain of Responsibility
Chain of Responsibility passes a request along a series of handlers until one processes it, decoupling the sender from the receiver. It powers middleware pipelines and approval flows but risks unhandled requests and harder debugging.
Chain of Responsibility is a behavioral pattern that lets a request travel along a chain of handler objects until one of them handles it. The sender does not need to know which handler will respond; it simply hands the request to the first link. Each handler decides whether to process the request, pass it along, or both. This decouples the originator of a request from the code that ultimately services it.
How It Works
A handler interface defines a method to process a request and a reference to the next handler. Each concrete handler implements the method: if it can deal with the request, it does so; otherwise it delegates to its successor. Handlers are linked into a chain, often built dynamically. A request enters at the head and moves along until a handler consumes it or the chain ends. Variants allow a handler to do partial work and still forward the request, which turns the chain into a processing pipeline.
The chain can be reconfigured at runtime by reordering, inserting, or removing handlers, giving flexible, composable control flow.
When to Use It
Use Chain of Responsibility when more than one object may handle a request and the handler is not known in advance, when you want to issue a request to one of several handlers without coding the choice explicitly, or when the set of handlers should be configurable at runtime. It models event bubbling, approval workflows, logging levels, and especially middleware pipelines in web frameworks.
Trade-offs
A request can fall off the end of the chain unhandled, so a default or terminal handler is wise. Debugging is harder because control passes through several objects and the path is not obvious from the call site. Long chains can hurt performance and make it unclear which handler did the work. Order matters and is easy to get wrong. The pattern offers no guarantee that any handler will respond.
Related Patterns
Command can represent the request that travels the chain. Decorator has a similar structure of wrapped objects but always calls the wrapped object, whereas a chain handler may stop the request short. Pipes and Filters is the data-processing cousin where every stage transforms and forwards the data rather than choosing whether to handle it. Composite can build the chain itself. The pattern shows up in event propagation through nested UI elements, in exception handling where each handler decides whether to catch or rethrow, in logging frameworks that pass a record up through level handlers, and in approval workflows where a request escalates from manager to director to vice-president until someone has the authority to decide.
Example
An HTTP server processes each request through middleware: authentication, rate limiting, logging, then the route handler. Each middleware either rejects the request, enriches it and calls the next link, or both. Adding CORS handling means inserting one handler at the right position, with no change to the others or to the route logic. This middleware model is the dominant expression of the pattern in modern web frameworks across languages, and its chains are usually assembled at startup from configuration so the same server can be composed differently per environment. A terminal default handler at the end of the chain guarantees that an unmatched request produces a clear not-found response rather than silently vanishing, which is the standard guard against the pattern's main failure mode.