Skip to content

// SOLID & Principles · 01

Single Responsibility

One class, many reasonsleads toOne reason to change

A module answers to one actor, so it has one reason to change.

How it fits together

Single Responsibility structureBeforeAfter«client»CheckoutControllerOrderServicePriceOrder(order)SaveOrder(order)SendConfirmation(order)OrderServicePlaceOrder(order)PricingRulesTotal(order)OrderStoreSave(order)EmailNotifierConfirm(order)1 price2 save3 email
uses / calls· numbered steps replay in order
  • CheckoutController uses / calls OrderService
  • OrderService uses / calls PricingRules (1 price)
  • OrderService uses / calls OrderStore (2 save)
  • OrderService uses / calls EmailNotifier (3 email)

Spot it

  • Unrelated changes keep touching the same class
  • Two teams conflict in the same file
  • Tests mock things the case doesn't use

It costs

More, smaller types to navigate; split too finely and one change spans many files.

In code

public sealed class OrderService(PricingRules pricing, OrderStore store, EmailNotifier email){    public async Task PlaceOrderAsync(Order order, CancellationToken ct)    {        order.Total = pricing.Total(order);        await store.SaveAsync(order, ct);        await email.ConfirmAsync(order, ct);    }}​public sealed class PricingRules{    public decimal Total(Order order) =>        order.Lines.Sum(line => line.UnitPrice * line.Quantity);}
Go deeperThe full explanation and what to ask for · 1 min readExpand

Robert Martin's phrasing: a module should be responsible to one, and only one, actor. The unit is the reason to change, which usually maps to a person or group who asks for changes.

It is not "do one thing". A class can have many methods and still have one reason to change; a single method can mix two, like deciding what goes in a report and how it is formatted.

Split when two actors actually pull the same code in different directions, not preemptively. Cohesion is the goal: things that change together stay together.

Ask for it

  • reason to change
  • actor
  • cohesion
  • separation of concerns
  • extract class

Split OrderService by reason to change: pricing rules, persistence and email notifications each get their own class behind an interface, wired through the DI container. Keep the public behaviour identical and say which actor owns each new class.