ProgramGuru

Chain of Responsibility Pattern

Chain of Responsibility: start from the problem

See the responsibilities

RequestEnters a defined pipeline
ValidationMay reject malformed input
PermissionMay reject an unauthorized action
HandlerActs only after required checks
Request: Enters a defined pipeline → Validation: May reject malformed input → Permission: May reject an unauthorized action → Handler: Acts only after required checks

A request must pass validation and permission checks before a handler acts. Hard-coding all checks into one operation makes it difficult to understand which step stops a request.

This is a behavioral pattern lesson. Follow the responsibilities, inspect the small JavaScript example and compare the design choices. The example is local teaching code; its boundary is explained before you apply the idea to a real service.

What you’ll learn

  • Explain the problem Chain of Responsibility addresses and the roles involved.
  • Follow the example and distinguish this pattern from nearby alternatives.
  • Choose when the benefit justifies the extra responsibilities and testing.

How the responsibilities work

Chain of Responsibility passes a request through ordered handlers. Each can handle it, reject it or forward it under a shared rule. Some chains stop at the first matching handler; middleware pipelines often run several handlers before a final action. State which variant you use.

Our pipeline checks name and permission before returning success. A rejected request stops the chain. The example does not implement production authentication; it illustrates ordered responsibility.

Follow a small example

Read the JavaScript from top to bottom and compare the expected output. The values are chosen to make this pattern’s responsibility visible. This example runs in one local execution context; it does not create remote services, durable storage or production access controls.

JavaScript · local teaching example

const handlers = [
  request => request.name ? null : 'missing name',
  request => request.allowed ? null : 'forbidden'
];
function handle(request) {
  for (const check of handlers) { const error = check(request); if (error) return error; }
  return 'accepted';
}
console.log(handle({name: 'order', allowed: false}));
console.log(handle({name: 'order', allowed: true}));

Expected output

forbidden
accepted

Choose a design and inspect the consequence

Compare the design choices

A security check rejects the operation.
Handlers may transform request values.

Compare the visible consequences. With JavaScript enabled, choose an option in each scenario to assemble your decision record.

This is an exploratory design exercise. It does not run infrastructure, measure capacity or replace the lesson’s quiz.

Use the consequences to explain your decision, including the simpler alternative.

Apply the pattern to two situations. Select one option in each situation to build a decision record. Every option and its consequence stays visible, and you can change your choices.

When it helps—and what it costs

Use it for a clear ordered set of independent checks or routing handlers. Record which handlers may terminate, transform or invoke the next stage.

Ordering can change security and behaviour. Missing forwarding can silently drop work; forwarding twice can duplicate effects. Long chains make ownership difficult, so use clear stop rules and instrumentation.

Distinguish the neighbouring patterns

Decorator wraps an object contract and can surround a delegated operation. Chain handlers decide where a request proceeds. Mediator coordinates relationships among participants rather than only a linear pipeline.

Follow wrapping behaviour around delegation, coordination among participants, gateway checks and routing to compare the specific responsibility before combining patterns.

Check your pattern reasoning

Why does the forbidden request not reach the successful outcome?
Choose one answer. You can try again.

Answer and explanation

A handler returns a rejection and stops the pipeline.

The chain has an explicit stop rule.

Check your pattern reasoning

What can calling the next handler twice cause?
Choose one answer. You can try again.

Answer and explanation

Repeated downstream effects.

Forwarding is an execution responsibility.

Name the responsibility before choosing the pattern

For Chain of Responsibility, describe the problem, the participating roles and the observable contract. Use the example as a starting point and test the boundaries identified in the trade-offs. A pattern name alone does not establish correctness or performance.

Return to the design-pattern guide to compare families, or apply the idea alongside gateway checks and routing.

Reaching the recap and completing the quizzes are separate milestones.

    Next lesson: Mediator Pattern →