<CodeChronicles/>
← Back to System Design Plan
Day 1SOLID Design Principles

Day 1 — Single Responsibility Principle (SRP)

2026-06-30·7 min read·✅ Completed

Day 1 — Single Responsibility Principle (SRP)

📋 Topics Covered

  • What is SOLID and why interviewers care
  • S — Single Responsibility Principle (SRP)
    • One class = one reason to change
    • Bad vs good example
  • How SRP relates to cohesion, coupling, and separation of concerns
  • Identifying SRP violations
  • Refactoring techniques
  • Practical checklist to achieve SRP in real code

📚 My Learning Notes

What is SRP?

A class should have one, and only one, reason to change.

In simple terms: Each class should do ONE thing and do it well.

Why It Matters

  • Easier to understand and maintain
  • Easier to test
  • Changes in one area don't break other areas
  • Better code organization

SRP, Cohesion, Coupling, and Separation of Concerns

These concepts are tightly connected and often discussed together in interviews.

1) SRP and Cohesion (aim for high cohesion)

  • Cohesion = how strongly related the responsibilities inside a class/module are.
  • A class following SRP usually has high cohesion because its methods and fields all support one clear purpose.
  • If a class has methods that feel unrelated, cohesion is low and SRP is likely violated.

Quick test: Can you describe the class without using "and"?

  • Good: "This class calculates invoice totals."
  • Risky: "This class calculates totals and sends emails and writes to DB."

2) SRP and Coupling (aim for low coupling)

  • Coupling = how much one class depends on others.
  • SRP helps reduce unnecessary coupling by separating responsibilities into focused components.
  • When responsibilities are mixed, every change can ripple through multiple dependencies.

Example:

  • If Invoice both calculates totals and sends emails, it now depends on pricing rules + email infrastructure.
  • Splitting those concerns keeps domain logic independent from infrastructure details.

3) SRP and Separation of Concerns (SoC)

  • SoC = divide software into distinct concerns (domain, persistence, UI, notifications, etc.).
  • SRP is a class-level/application-level way to enforce SoC.
  • You can think of SRP as: "SoC applied to objects/components."

Rule of thumb:

  • SoC tells you what to separate.
  • SRP tells you how small and focused each unit should be.

Common Violations

  1. God Classes — Classes that do everything
  2. Manager Classes — Classes with too many responsibilities
  3. Utility Classes — Classes that mix unrelated functionality
  4. Multi-layer classes — One class mixing domain logic + database + HTTP/email

💻 Practice Code

❌ Bad Example (SRP Violation)

// This Invoice class has TOO MANY responsibilities
public class Invoice {
    private String customer;
    private double amount;
    
    // Responsibility 1: Calculate invoice
    public double calculateTotal() {
        return amount * 1.1; // including 10% tax
    }
    
    // Responsibility 2: Save to database
    public void saveToDB() {
        // database logic here
        System.out.println("Saving to DB...");
    }
    
    // Responsibility 3: Print invoice
    public void printInvoice() {
        System.out.println("Printing invoice...");
    }
    
    // Responsibility 4: Send email
    public void sendEmail() {
        System.out.println("Sending email...");
    }
}

Problems:

  • If DB schema changes, Invoice class must change
  • If email format changes, Invoice class must change
  • If printing format changes, Invoice class must change
  • Hard to test each function independently

✅ Good Example (Following SRP)

// 1. Invoice class — only handles invoice data and calculation
public class Invoice {
    private String customer;
    private double amount;
    
    public double calculateTotal() {
        return amount * 1.1;
    }
    
    public String getCustomer() {
        return customer;
    }
    
    public double getAmount() {
        return amount;
    }
}

// 2. InvoiceRepository — handles database operations
public class InvoiceRepository {
    public void save(Invoice invoice) {
        // database logic
        System.out.println("Saving to DB: " + invoice.getCustomer());
    }
}

// 3. InvoicePrinter — handles printing
public class InvoicePrinter {
    public void print(Invoice invoice) {
        System.out.println("--- INVOICE ---");
        System.out.println("Customer: " + invoice.getCustomer());
        System.out.println("Total: $" + invoice.calculateTotal());
    }
}

// 4. EmailService — handles email notifications
public class EmailService {
    public void sendInvoiceEmail(Invoice invoice) {
        System.out.println("Sending email to: " + invoice.getCustomer());
    }
}

// Usage
public class Main {
    public static void main(String[] args) {
        Invoice invoice = new Invoice("John Doe", 100.0);
        
        InvoiceRepository repo = new InvoiceRepository();
        repo.save(invoice);
        
        InvoicePrinter printer = new InvoicePrinter();
        printer.print(invoice);
        
        EmailService emailService = new EmailService();
        emailService.sendInvoiceEmail(invoice);
    }
}

Benefits:

  • Each class has ONE reason to change
  • Easy to test each class independently
  • Can replace implementation without affecting others
  • Clear separation of concerns

🛠️ How to Achieve SRP in Real Projects

Step-by-step approach

  1. List reasons to change for a class (business rule, schema change, API contract, formatting, etc.).
  2. If reasons come from different actors/teams (e.g., product vs DBA vs DevOps), split responsibilities.
  3. Extract by concern: domain logic, persistence, communication, presentation, validation.
  4. Keep the domain model focused on business behavior.
  5. Move side effects (DB, email, file I/O, external APIs) to dedicated services/adapters.
  6. Add tests per responsibility to lock behavior.

Practical heuristics

  • If a class name ends with vague words like Manager, Helper, Processor, double-check SRP.
  • If a class imports too many unrelated libraries (DB + mail + HTTP + formatting), it's a smell.
  • If one change request regularly modifies the same giant class, split it.
  • Prefer composition over bloated inheritance hierarchies.

Avoid over-splitting

  • SRP is not "one method per class".
  • Keep related behavior together when it changes for the same reason.
  • Goal: focused modules, not a class explosion.

🎯 Practice Exercise

Exercise 1: Identify SRP Violations

Look at this User class — what responsibilities does it have?

public class User {
    private String name;
    private String email;
    
    public void registerUser() { /* ... */ }
    public void sendWelcomeEmail() { /* ... */ }
    public void logActivity() { /* ... */ }
    public void validateEmail() { /* ... */ }
    public void saveToDatabase() { /* ... */ }
}

My Answer:

  • User registration logic
  • Email sending
  • Logging
  • Validation
  • Database operations

Refactored structure:

  • User — data only
  • UserRegistrationService — handles registration
  • EmailService — sends emails
  • Logger — logs activities
  • EmailValidator — validates emails
  • UserRepository — database operations

Exercise 2: Real-World Example

Before (Violation):

// My ProductService was doing too much
public class ProductService {
    public void createProduct() { }
    public void sendInventoryAlert() { }
    public void generateReport() { }
    public void calculateDiscount() { }
}

After (SRP):

public class ProductService {
    public void createProduct() { }
}

public class InventoryAlertService {
    public void sendAlert() { }
}

public class ReportGenerator {
    public void generate() { }
}

public class DiscountCalculator {
    public double calculate() { }
}

📝 Key Takeaways

  1. One class = one responsibility = one reason to change
  2. SRP drives high cohesion and supports low coupling
  3. SRP is a practical form of separation of concerns at class/module level
  4. If you use "and" when describing what a class does, it may be doing too much
  5. Ask: "Why would this class need to change?" If multiple unrelated reasons → split it
  6. SRP makes code easier to test, maintain, and evolve safely

✅ Checklist

  • Understand what SRP means
  • Understand SRP relation to cohesion, coupling, and SoC
  • Identify SRP violations in code
  • Know how to refactor to follow SRP
  • Practiced with Invoice example
  • Applied to real-world code from my projects

🔗 References

  • Clean Code by Robert C. Martin
  • Design Gurus: Grokking SOLID Design Principles
  • Refactoring Guru: SOLID Principles

Status: ✅ Completed on 2026-06-30
Time Spent: 1.5 hours
Next: Day 2 — Open/Closed Principle (OCP)