Bird
Raised Fist0
LLDsystem_design~12 mins

Chain of Responsibility pattern in LLD - Architecture Diagram

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
System Overview - Chain of Responsibility pattern

The Chain of Responsibility pattern allows a request to pass through a chain of handlers. Each handler decides to process the request or pass it to the next handler. This design helps separate concerns and makes the system flexible to add or change handlers without modifying the client.

Architecture Diagram
User
  |
  v
Handler 1 ---> Handler 2 ---> Handler 3 ---> Null Handler
  |
Response
Components
User
client
Initiates the request to be handled
Handler 1
handler
First handler in the chain; processes or forwards request
Handler 2
handler
Second handler; processes or forwards request
Handler 3
handler
Third handler; processes or forwards request
Null Handler
handler
End of chain; handles unprocessed requests or returns default response
Request Flow - 8 Hops
UserHandler 1
Handler 1Handler 1
Handler 1Handler 2
Handler 2Handler 2
Handler 2Handler 3
Handler 3Handler 3
Handler 3Null Handler
Null HandlerUser
Failure Scenario
Component Fails:Handler 2
Impact:Requests that require Handler 2's processing are not handled and passed down the chain, possibly causing delays or incorrect handling.
Mitigation:Implement fallback logic in Handler 3 or Null Handler to handle requests if Handler 2 fails; monitor handler health and remove or restart failing handler.
Architecture Quiz - 3 Questions
Test your understanding
What happens if Handler 1 cannot process a request?
AIt forwards the request to Handler 2
BIt returns an error to the user
CIt processes the request anyway
DIt stops the request chain
Design Principle
The Chain of Responsibility pattern decouples sender and receiver by passing requests along a chain of handlers. This allows flexible and dynamic handling of requests without tight coupling, improving maintainability and scalability.

Practice

(1/5)
1. What is the main purpose of the Chain of Responsibility pattern in system design?
easy
A. To pass a request along a chain of handlers until one handles it
B. To create multiple copies of an object
C. To ensure only one instance of a class exists
D. To define a family of algorithms and make them interchangeable

Solution

  1. Step 1: Understand the pattern's behavior

    The Chain of Responsibility pattern allows a request to be passed along a chain of objects until one can handle it.
  2. Step 2: Compare options with the pattern's purpose

    The options describing singleton (one instance), prototype (object copies), and strategy (interchangeable algorithms) refer to other design patterns, not Chain of Responsibility.
  3. Final Answer:

    To pass a request along a chain of handlers until one handles it -> Option A
  4. Quick Check:

    Chain of Responsibility = pass request along chain [OK]
Hint: Remember: Chain passes request until handled [OK]
Common Mistakes:
  • Confusing with Singleton or Strategy patterns
  • Thinking it creates object copies
  • Assuming it handles all requests at once
2. Which of the following is the correct way to link handlers in a Chain of Responsibility pattern?
easy
A. Handlers are linked using a global static list
B. Handlers are independent and do not reference each other
C. Handlers communicate through a shared database
D. Each handler holds a reference to the next handler in the chain

Solution

  1. Step 1: Recall how handlers are connected

    In Chain of Responsibility, each handler has a reference to the next handler to pass the request along.
  2. Step 2: Evaluate other options

    Global static lists, shared databases, and independent handlers describe unrelated or incorrect linking methods that contradict the chain concept.
  3. Final Answer:

    Each handler holds a reference to the next handler in the chain -> Option D
  4. Quick Check:

    Handler links = next handler reference [OK]
Hint: Handlers link by referencing the next handler [OK]
Common Mistakes:
  • Using global lists instead of direct references
  • Assuming handlers are independent
  • Confusing with event broadcasting
3. Consider this simplified handler chain code snippet:
class Handler:
    def __init__(self, successor=None):
        self.successor = successor
    def handle(self, request):
        if self.can_handle(request):
            return f"Handled {request}"
        elif self.successor:
            return self.successor.handle(request)
        else:
            return "Not handled"
    def can_handle(self, request):
        return False

class ConcreteHandlerA(Handler):
    def can_handle(self, request):
        return request == 'A'

class ConcreteHandlerB(Handler):
    def can_handle(self, request):
        return request == 'B'

chain = ConcreteHandlerA(ConcreteHandlerB())
print(chain.handle('B'))

What is the output of this code?
medium
A. Handled B
B. Handled A
C. Not handled
D. Error

Solution

  1. Step 1: Trace the request through the chain

    The request 'B' is first checked by ConcreteHandlerA, which returns False for can_handle('B'). It passes the request to ConcreteHandlerB.
  2. Step 2: ConcreteHandlerB handles the request

    ConcreteHandlerB's can_handle('B') returns True, so it returns "Handled B".
  3. Final Answer:

    Handled B -> Option A
  4. Quick Check:

    Request 'B' handled by second handler [OK]
Hint: Request passes chain until a handler returns true [OK]
Common Mistakes:
  • Assuming first handler handles all requests
  • Confusing return values
  • Missing the chain passing logic
4. Given the following code snippet, identify the bug that breaks the Chain of Responsibility pattern:
class Handler:
    def __init__(self, successor=None):
        self.successor = successor
    def handle(self, request):
        if self.can_handle(request):
            return f"Handled {request}"
        else:
            return "Not handled"
    def can_handle(self, request):
        return False

class ConcreteHandler(Handler):
    def can_handle(self, request):
        return request == 'X'

chain = ConcreteHandler()
print(chain.handle('Y'))

What is the main issue here?
medium
A. The successor is not initialized properly
B. The can_handle method always returns True
C. The handler does not pass the request to the successor
D. The handle method has a syntax error

Solution

  1. Step 1: Analyze the handle method logic

    The handle method checks can_handle; if False, it returns "Not handled" immediately without passing to successor.
  2. Step 2: Identify missing chain passing

    It should call self.successor.handle(request) if successor exists, but this is missing.
  3. Final Answer:

    The handler does not pass the request to the successor -> Option C
  4. Quick Check:

    Missing successor call breaks chain [OK]
Hint: Always pass request to successor if not handled [OK]
Common Mistakes:
  • Forgetting to call successor.handle()
  • Assuming can_handle always returns True
  • Ignoring successor initialization
5. You are designing a logging system using the Chain of Responsibility pattern. You want to handle logs of different severity: DEBUG, INFO, WARNING, ERROR. Each handler should process logs at its level and pass higher severity logs down the chain. Which design best fits this requirement?
hard
A. Each handler processes only its level and passes all logs to the next handler
B. Each handler processes logs at its level and passes only higher severity logs to the next handler
C. Each handler processes all logs regardless of severity and stops the chain
D. Each handler processes logs randomly without order

Solution

  1. Step 1: Understand the logging severity flow

    Logs should be handled at their level, and higher severity logs should continue down the chain for further handling.
  2. Step 2: Evaluate options for correct chain behavior

    Each handler processes logs at its level and passes only higher severity logs to the next handler matches this: handlers process their level and pass higher severity logs onward. Each handler processes only its level and passes all logs to the next handler passes all logs regardless, which is inefficient. Each handler processes all logs regardless of severity and stops the chain stops chain prematurely. Each handler processes logs randomly without order is random and incorrect.
  3. Final Answer:

    Each handler processes logs at its level and passes only higher severity logs to the next handler -> Option B
  4. Quick Check:

    Process level, pass higher severity [OK]
Hint: Process own level, pass higher severity logs down [OK]
Common Mistakes:
  • Passing all logs without filtering
  • Stopping chain too early
  • Ignoring severity order