← Back to Article List         
Single Responsibility Principle (SRP)

Single Responsibility Principle (SRP)

Published on 28 Sep 2026     9 min read SOLID Principles
SOLID Principles

Single Responsibility Principle (SRP)

1. What is Single Responsibility Principle?

Single Responsibility Principle (SRP) is the first principle of SOLID.

A class should have only one reason to change.

In simple English:

One class should focus on one responsibility.

It does not mean a class should contain only one method. A class can have multiple methods, but those methods should belong to the same responsibility.


2. Why do we need SRP?

Suppose we have an employee application.

We create one class called:

EmployeeService

and put everything inside it:

EmployeeService
    │
    ├── Add Employee
    ├── Calculate Salary
    ├── Save Employee to Database
    ├── Send Email
    └── Generate Report

This class is doing too many different jobs.

Now imagine these changes:

  • Database changes from SQL Server to another database
  • Email provider changes
  • Salary calculation changes
  • Report format changes

Every change requires us to modify the same EmployeeService.

That means:

EmployeeService
      ↓
Too many responsibilities
      ↓
Too many reasons to change
      ↓
Harder to maintain
      ↓
Higher chance of bugs

SRP tells us to separate these responsibilities.


3. Simple Scenario

Let's build a simple Order Processing application.

Our requirements are:

  1. Calculate order total.
  2. Save the order.
  3. Send confirmation email.

First, let's see a design that violates SRP.


4. Before SRP — Bad Design

using System;

public class OrderService
{
    public void ProcessOrder(string customerEmail, decimal amount)
    {
        // Responsibility 1: Calculate total
        decimal tax = amount * 0.10m;
        decimal total = amount + tax;

        Console.WriteLine($"Order Total: {total}");

        // Responsibility 2: Save order
        Console.WriteLine("Order saved to database.");

        // Responsibility 3: Send email
        Console.WriteLine(
            $"Confirmation email sent to {customerEmail}");
    }
}

public class Program
{
    public static void Main()
    {
        OrderService orderService = new OrderService();

        orderService.ProcessOrder(
            "customer@gmail.com",
            1000);
    }
}

Output

Order Total: 1100.00
Order saved to database.
Confirmation email sent to customer@gmail.com

The program works.

But the design has a problem.


5. What is the problem?

OrderService is doing three different responsibilities:

                OrderService
                     │
        ┌────────────┼────────────┐
        ↓            ↓            ↓
   Calculate      Database       Email
     Total          Save          Send

So there are three different reasons why OrderService might need to change.

Reason 1 — Business calculation changes

Today:

Tax = 10%

Tomorrow:

Tax = 18%

We modify OrderService.

Reason 2 — Database changes

Today:

SQL Server

Tomorrow:

Azure SQL

Again, we modify OrderService.

Reason 3 — Email changes

Today:

Gmail SMTP

Tomorrow:

SendGrid

Again, we modify OrderService.

So:

                    OrderService
                         │
        ┌────────────────┼────────────────┐
        ↓                ↓                ↓
 Tax calculation      Database          Email
    changes            changes          changes
        │                │                │
        └────────────────┼────────────────┘
                         ↓
               Modify OrderService

This is what SRP tries to avoid.


6. Apply Single Responsibility Principle

We separate the responsibilities into different classes.

OrderCalculator
       ↓
Calculate order total


OrderRepository
       ↓
Save order


EmailService
       ↓
Send email


OrderService
       ↓
Coordinate order processing

Each class now has a clear responsibility.


7. Full Simple Program Using SRP

using System;

// Responsibility 1:
// Calculate order total
public class OrderCalculator
{
    public decimal CalculateTotal(decimal amount)
    {
        decimal tax = amount * 0.10m;

        return amount + tax;
    }
}


// Responsibility 2:
// Save order
public class OrderRepository
{
    public void Save(decimal total)
    {
        Console.WriteLine(
            $"Order saved. Total: {total}");
    }
}


// Responsibility 3:
// Send email
public class EmailService
{
    public void SendConfirmation(
        string email,
        decimal total)
    {
        Console.WriteLine(
            $"Confirmation email sent to {email}. Total: {total}");
    }
}


// Responsibility:
// Coordinate the order process
public class OrderService
{
    private readonly OrderCalculator _calculator;
    private readonly OrderRepository _repository;
    private readonly EmailService _emailService;

    public OrderService(
        OrderCalculator calculator,
        OrderRepository repository,
        EmailService emailService)
    {
        _calculator = calculator;
        _repository = repository;
        _emailService = emailService;
    }

    public void ProcessOrder(
        string customerEmail,
        decimal amount)
    {
        decimal total =
            _calculator.CalculateTotal(amount);

        Console.WriteLine(
            $"Order Total: {total}");

        _repository.Save(total);

        _emailService.SendConfirmation(
            customerEmail,
            total);
    }
}


// Program
public class Program
{
    public static void Main()
    {
        OrderCalculator calculator =
            new OrderCalculator();

        OrderRepository repository =
            new OrderRepository();

        EmailService emailService =
            new EmailService();

        OrderService orderService =
            new OrderService(
                calculator,
                repository,
                emailService);

        orderService.ProcessOrder(
            "customer@gmail.com",
            1000);
    }
}

Output

Order Total: 1100.00
Order saved. Total: 1100.00
Confirmation email sent to customer@gmail.com. Total: 1100.00

8. Program Explanation

Now let's understand the responsibility of each class.

OrderCalculator

public class OrderCalculator
{
    public decimal CalculateTotal(decimal amount)
    {
        decimal tax = amount * 0.10m;

        return amount + tax;
    }
}

Its only responsibility is:

Order calculation

It doesn't know anything about:

  • Database
  • Email
  • Saving orders

If tax calculation changes, we mainly change this class.


9. OrderRepository

public class OrderRepository
{
    public void Save(decimal total)
    {
        Console.WriteLine(
            $"Order saved. Total: {total}");
    }
}

Its responsibility is:

Saving the order

In a real application, this class might use:

  • Entity Framework Core
  • SQL Server
  • Dapper

It doesn't calculate tax.

It doesn't send emails.


10. EmailService

public class EmailService
{
    public void SendConfirmation(
        string email,
        decimal total)
    {
        Console.WriteLine(
            $"Confirmation email sent to {email}. Total: {total}");
    }
}

Its responsibility is:

Sending email

If the email implementation changes, we modify the email-related class rather than putting that logic inside the order calculation or database code.


11. What about OrderService?

You may ask:

OrderService is calling three classes. Doesn't that mean it has three responsibilities?

No.

Its responsibility is:

Coordinate the order-processing workflow.

public void ProcessOrder(
    string customerEmail,
    decimal amount)
{
    decimal total =
        _calculator.CalculateTotal(amount);

    _repository.Save(total);

    _emailService.SendConfirmation(
        customerEmail,
        total);
}

It does not itself perform all those technical responsibilities.

It coordinates them.

Think about a manager.

A manager may tell:

Accountant → Calculate amount

Database team → Save information

Email team → Send confirmation

The manager's responsibility is coordination, not performing every specialist's job.


12. Flow of the Program

Program
   │
   ↓
OrderService.ProcessOrder()
   │
   ├───────────────→ OrderCalculator
   │                  │
   │                  ↓
   │              Calculate Total
   │                  │
   │                  ↓
   │               1100.00
   │
   ├───────────────→ OrderRepository
   │                  │
   │                  ↓
   │               Save Order
   │
   └───────────────→ EmailService
                      │
                      ↓
               Send Confirmation

The important point is that each component has a clear purpose.


13. Before SRP vs After SRP

Before

                    OrderService
                         │
             ┌───────────┼───────────┐
             ↓           ↓           ↓
         Calculate      Save        Email

OrderService contains all the logic.

After

                     OrderService
                          │
          ┌───────────────┼───────────────┐
          ↓               ↓               ↓
 OrderCalculator    OrderRepository   EmailService
          │               │               │
          ↓               ↓               ↓
      Calculate          Save           Email

Now responsibilities are separated.


14. Does SRP Mean "One Method Per Class"?

No.

This is a common misunderstanding.

For example:

public class EmployeeRepository
{
    public void Add()
    {
    }

    public void Update()
    {
    }

    public void Delete()
    {
    }

    public void GetById()
    {
    }
}

This class contains several methods.

That doesn't automatically violate SRP because they all belong to one responsibility:

Employee data persistence

So remember:

One class = One method

is wrong.

Instead:

One class = One cohesive responsibility

is the better idea.


15. "One Reason to Change" — Important Interview Concept

This is the most important part of SRP.

Consider:

InvoiceService

If it handles:

Calculate Invoice
Save Invoice
Generate PDF
Send Email

then it can change because of:

Accounting rules changed
Database changed
PDF format changed
Email provider changed

That's multiple independent reasons to change.

After SRP:

InvoiceCalculator
    ↓
Accounting changes


InvoiceRepository
    ↓
Database changes


InvoicePdfGenerator
    ↓
PDF changes


EmailService
    ↓
Email changes

Now each class has a much clearer reason to change.


16. Real-Time ASP.NET Core Example

In an ASP.NET Core application, you might see:

OrderController
       │
       ↓
OrderService
       │
       ├──→ OrderRepository
       │
       ├──→ PaymentService
       │
       └──→ EmailService

Responsibilities could be:

Class Responsibility
OrderController Handle HTTP request/response
OrderService Coordinate order business workflow
OrderRepository Order database operations
PaymentService Payment processing
EmailService Email communication

This separation is common in real enterprise applications.


17. Advantages of SRP

1. Easy to Maintain

If email logic changes:

Change EmailService

You don't need to modify unrelated order calculation logic.


2. Easy to Test

You can test:

OrderCalculator

separately from:

EmailService
OrderRepository

This makes unit testing simpler.


3. Easy to Debug

If the order total is wrong, you know to investigate:

OrderCalculator

If email is not working:

EmailService

The responsibility boundary helps narrow down problems.


4. Reduces Coupling

Classes have fewer unrelated dependencies and concerns.

This generally makes the system easier to change.


5. Better Readability

Compare:

OrderService
 ├── Calculate
 ├── Database
 ├── Email
 ├── PDF
 ├── Logging
 ├── SMS
 └── Payment

with:

OrderCalculator
OrderRepository
EmailService
PdfService
PaymentService

The second design communicates responsibilities more clearly.


6. Easier Team Development

Different developers can work on different areas:

Developer A → PaymentService

Developer B → OrderRepository

Developer C → EmailService

Clear boundaries can reduce unnecessary overlap.


7. Easier Future Changes

Suppose tomorrow:

10% Tax
   ↓
18% Tax

The calculation-related class is the natural place to make that change.


18. Can SRP Be Overused?

Yes.

You should not create a separate class for every tiny method just because SRP exists.

Bad interpretation:

AddEmployeeClass
UpdateEmployeeClass
DeleteEmployeeClass
GetEmployeeClass

That may create unnecessary complexity.

Instead, if all these operations belong naturally together:

EmployeeRepository
 ├── Add()
 ├── Update()
 ├── Delete()
 └── Get()

can still have a single cohesive responsibility:

Employee persistence

SRP is about responsibility, not the number of methods.


19. How to Identify an SRP Violation

When reviewing a class, ask:

Question 1:

What is this class responsible for?

If your answer contains many unrelated responsibilities, investigate further.

For example:

"It calculates invoices, saves them, sends email, generates PDF and writes audit logs."

That is a warning sign.

Question 2:

What different business or technical changes could force this class to change?

If you find many unrelated reasons, the class may violate SRP.


20. Key Points

  • SRP = Single Responsibility Principle.
  • It is the S in SOLID.
  • A class should have one primary reason to change.
  • SRP does not mean one method per class.
  • Multiple related methods can exist in the same class.
  • Separate unrelated responsibilities such as business calculation, database access, email, reporting, and file generation.
  • SRP improves maintainability, readability, testability, and change isolation.
  • A service can coordinate several other services and still follow SRP when coordination itself is its responsibility.
  • Don't split classes unnecessarily; the goal is high cohesion, not maximum number of classes.

Interview Answer

Single Responsibility Principle states that a class should have only one reason to change. In simple terms, each class should focus on one cohesive responsibility. For example, order calculation, database persistence, and email notification should generally be handled by separate components rather than putting all the logic into one class. This makes the application easier to maintain, test, debug, and extend.