0
0
LLDsystem_design~7 mins

Single Responsibility Principle in LLD - System Design Guide

Choose your learning style9 modes available
Problem Statement
When a class or module handles multiple responsibilities, changes in one area can cause unexpected bugs in others. This makes the code hard to understand, test, and maintain, slowing down development and increasing errors.
Solution
Divide the code so each class or module has only one reason to change by focusing on a single responsibility. This keeps code simpler, easier to test, and safer to modify without affecting unrelated parts.
Architecture
Bad Design
Multi-task
Handles User
Data & Email
Handles Email
Handles Email

The diagram shows a single class doing multiple jobs versus splitting those jobs into separate classes each with one responsibility.

Trade-offs
✓ Pros
Improves code readability by focusing each class on one task.
Makes testing easier since each class has a clear purpose.
Reduces risk of bugs when changing code because responsibilities are isolated.
Facilitates parallel development as different developers can work on separate classes.
✗ Cons
May increase the number of classes, making the project structure larger.
Requires careful design upfront to identify distinct responsibilities.
Can introduce more files and interfaces, which might seem complex initially.
Use when classes or modules start to grow large or handle multiple unrelated tasks, especially in projects expected to evolve or be maintained long-term.
Avoid in very small scripts or prototypes where simplicity and speed of writing code outweigh long-term maintainability.
Real World Examples
Amazon
Amazon separates order processing, payment handling, and notification sending into different services to allow independent updates and scaling.
Netflix
Netflix uses single responsibility in microservices to isolate user management, streaming, and recommendation logic, enabling faster deployment and fewer bugs.
Spotify
Spotify splits music playback, user playlists, and social sharing into distinct components to simplify development and testing.
Code Example
The before code mixes user data saving and email sending in one class, violating single responsibility. The after code splits these into two classes, each handling one responsibility, making the code easier to maintain and test.
LLD
### Before applying Single Responsibility Principle
class UserManager:
    def __init__(self, user_data):
        self.user_data = user_data

    def save_user(self):
        # code to save user data
        pass

    def send_welcome_email(self):
        # code to send email
        pass


### After applying Single Responsibility Principle
class UserDataManager:
    def __init__(self, user_data):
        self.user_data = user_data

    def save_user(self):
        # code to save user data
        pass

class EmailSender:
    def send_welcome_email(self, user_email):
        # code to send email
        pass
OutputSuccess
Alternatives
Modular Monolith
Groups related responsibilities into modules within a single application instead of separate classes or services.
Use when: Choose when the system is not large enough to justify full microservices but still needs separation of concerns.
Facade Pattern
Provides a simplified interface to a complex subsystem but does not enforce single responsibility inside the subsystem.
Use when: Choose when you want to hide complexity but not necessarily separate responsibilities.
Summary
Single Responsibility Principle ensures each class has only one reason to change.
It improves code clarity, testability, and reduces bugs by isolating responsibilities.
Use it especially in growing or long-lived projects to keep code manageable.