Skip to content

Advanced Topics

  • Back to Chain of Responsibility Overview

    Return to the Chain of Responsibility overview page with all topics.

    Back to Overview


Overview

You can also build chains manually using the build_chain() function:

from cqrs.requests.cor_request_handler import build_chain

# Create handler instances
handler1 = CreditCardHandler()
handler2 = PayPalHandler()
handler3 = DefaultPaymentHandler()

# Build chain manually
chain = build_chain([handler1, handler2, handler3])

# Use the chain
result = await chain.handle(
    ProcessPaymentCommand(
        amount=100.0,
        payment_method="credit_card",
        user_id="user1",
    )
)

set_next(handler)

Sets the next handler in the chain. Returns the next handler to allow chaining:

handler1.set_next(handler2).set_next(handler3)

next(request)

Passes the request to the next handler in the chain:

async def handle(self, request: ProcessPaymentCommand) -> PaymentResult | None:
    if can_handle(request):
        return process(request)

    # Pass to next handler
    return await self.next(request)

events Property

Returns the list of events generated by the handler. Must be implemented:

@property
def events(self) -> list[cqrs.Event]:
    return self._events.copy()

Chain of Responsibility is ideal for:

  • Payment processing — Try different payment methods in order
  • Authentication — Try multiple authentication strategies
  • Validation — Multiple validation rules with fallback
  • Error handling — Try different recovery strategies
  • Feature flags — Try different implementations based on availability
  • Rate limiting — Multiple rate limiters with fallback

  • Order matters — Place handlers in order of preference or priority

  • Always have a default — Include a default handler at the end of the chain
  • Clear decision logic — Make it obvious when a handler can process a request
  • Return early — Return immediately after processing, don't continue the chain
  • Use next() correctly — Only call next() when you can't handle the request
  • Handle events — Collect events from handlers that process requests
Feature Regular Handler COR Handler
Processing Single handler Multiple handlers in sequence
Decision Always processes Decides whether to process
Fallback Not available Built-in via chain
Use Case Simple operations Multiple strategies
Registration Single handler List of handlers

Chain of Responsibility works well with:

  • Dependency Injection — Handlers can be injected with dependencies
  • Event Handling — Handlers can emit events when processing requests
  • Middleware — Middleware can be applied to the entire chain
  • Regular Handlers — COR handlers can be used alongside regular handlers

The Chain of Responsibility pattern provides a flexible way to handle requests with multiple processing strategies. By linking handlers together, you can:

  • Try multiple strategies in order
  • Implement fallback mechanisms
  • Separate concerns across handlers
  • Easily extend functionality by adding new handlers