Single Responsibility Principle (SRP)
Every class has one reason to change — one responsibility, one job, one concern.
Intent & Description
🎯 Intent
Classes that handle multiple concerns get touched when any one of them changes — mixing responsibilities multiplies bug risk and makes every edit a refactor.
📋 Context
A single class handles validation, database access, email sending, and business logic. Change the email provider and you’re editing the same class as the database layer. Testing anything means pulling in everything.
💡 Solution
Split classes with multiple responsibilities into smaller, focused ones. Each class owns a single concern and changes for exactly one reason. User holds data. UserValidator validates. UserRepository persists. EmailService sends.
Real-world Use Case
📌 TL;DR
One class, one reason to change. Keep concerns separate or every edit becomes a game of Jenga.
Advantages
- One class, one purpose — easy to understand at a glance
- Reduced coupling between concerns
- Each responsibility is testable in isolation
- Smaller, more composable and reusable units
Disadvantages
- More classes overall — coordination becomes part of the design challenge
- Decomposition requires deliberate upfront thought
- Can feel like over-engineering when the class is still small
// Before: Class with multiple responsibilities
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
validate() {
// Validation logic
return this.email.includes('@');
}
save() {
// Database operations
console.log(`Saving ${this.name} to database`);
}
sendEmail() {
// Email sending logic
console.log(`Sending email to ${this.email}`);
}
}
// After: SRP applied
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
}
class UserValidator {
validate(user) {
return user.email.includes('@');
}
}
class UserRepository {
save(user) {
console.log(`Saving ${user.name} to database`);
}
}
class EmailService {
sendEmail(user) {
console.log(`Sending email to ${user.email}`);
}
}