← Back to scenarios
ScenarioCatalog DB

Form command chain

Check a form submission with sanitization, validation, and policy handlers before saving it.

Problem

A form workflow must apply ordered preprocessing and policy checks before persistence, with each handler able to reject invalid input or pass a valid submission to the next stage.

Related patterns

Implementations

Chain of Responsibility

TypeScriptApplication

Chain of Responsibility in TypeScript

Routes a support request through specialized handlers until one can resolve it, without coupling the requester to a specific handler.

TypeScript
type SupportRequest = {
  kind: "billing" | "technical" | "account";
  message: string;
};

abstract class SupportHandler {
  private next?: SupportHandler;

  setNext(handler: SupportHandler): SupportHandler {
    this.next = handler;
    return handler;
  }

  handle(request: SupportRequest): void {
    if (this.canHandle(request)) {
      this.resolve(request);
      return;
    }

    if (this.next) {
      this.next.handle(request);
      return;
    }

    console.log(`No handler found for: ${request.message}`);
  }

  protected abstract canHandle(request: SupportRequest): boolean;
  protected abstract resolve(request: SupportRequest): void;
}

class BillingHandler extends SupportHandler {
  protected canHandle(request: SupportRequest) {
    return request.kind === "billing";
  }

  protected resolve(request: SupportRequest) {
    console.log(`Billing resolved: ${request.message}`);
  }
}

class TechnicalHandler extends SupportHandler {
  protected canHandle(request: SupportRequest) {
    return request.kind === "technical";
  }

  protected resolve(request: SupportRequest) {
    console.log(`Technical support resolved: ${request.message}`);
  }
}

class AccountHandler extends SupportHandler {
  protected canHandle(request: SupportRequest) {
    return request.kind === "account";
  }

  protected resolve(request: SupportRequest) {
    console.log(`Account support resolved: ${request.message}`);
  }
}

const billing = new BillingHandler();
const technical = new TechnicalHandler();
const account = new AccountHandler();

billing.setNext(technical).setNext(account);

billing.handle({
  kind: "technical",
  message: "I cannot sign in to the dashboard.",
});

Associated technologies