Core Idea
Clients should not depend on methods they do not use.
The Violation
A monolithic UserRepository interface forces every consumer to depend on methods they don't need.
When to Split
| Signal | Action |
|---|---|
| Interface has "and" in its name | UserReadAndWriteAndAudit โ split |
| A method is only used by 20% of consumers | That method belongs in a separate interface |
| Changes to one method require recompiling many consumers | That consumer needs a narrower contract |
throw new UnsupportedOperationException in any implementation | The interface is too broad for that implementor |
Interview Checklist
- Start from USE CASES (what each client needs), not ENTITIES
- Design narrow interfaces around workflows
- Inject the smallest required contract in each service
- Combine interfaces only in concrete implementations (they implement multiple small interfaces)
Where This Appears in LLD Problems
- Splitwise (#5):
ExpenseReader,ExpenseWriter,BalanceCalculator(segregate from a monolithicExpenseManager) - ATM (#13):
CashDispenser,CardReader,AccountReader,TransactionWriter - Library Management (#12):
BookCatalogReader,BorrowingManager,NotificationDispatcher - Cab Sharing (#23):
TripReader,TripMatcher,PaymentCollector
The Fix
Split into narrow, role-based interfaces.
// โโโ EXAMPLE 1 โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// WHAT WE ARE IMPLEMENTING:
// A smart multifunction printer system where specialized machines implement
// only what they actually do.
//
// WHERE THE PRINCIPLE FITS IN:
// Split the monolithic Machine interface into highly cohesive interfaces:
// Printer, Scanner, and Fax. A basic printer implements only Printer,
// preventing unused methods.
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
import java.util.List;
// --- Bad: Fat interface ---
interface UserRepository {
User findById(String id);
void save(User user);
void delete(String id);
void update(User user);
List<User> findActiveUsers();
List<UserAudit> getAuditTrail(String userId);
AnalyticsReport getAnalytics(String query);
void exportToCSV();
}
// Every consumer depends on all methods, even if they only need one.
// --- Good: Segregated interfaces ---
interface UserReader {
User findById(String id);
List<User> findActiveUsers();
}
interface UserWriter {
void save(User user);
void update(User user);
void delete(String id);
}
interface UserAuditReader {
List<UserAudit> getAuditTrail(String userId);
}
interface UserAnalyticsReader {
AnalyticsReport getAnalytics(String query);
}
// --- Concrete implementation ---
class PostgresUserRepository implements UserReader, UserWriter, UserAuditReader, UserAnalyticsReader {
public User findById(String id) {
System.out.println(" [DB] SELECT * FROM users WHERE id = " + id);
return new User(id, "alice");
}
public void save(User user) { System.out.println(" [DB] INSERT INTO users ..."); }
public void update(User user) { System.out.println(" [DB] UPDATE users ..."); }
public void delete(String id) { System.out.println(" [DB] DELETE FROM users ..."); }
public List<User> findActiveUsers() { return List.of(new User("1", "alice"), new User("2", "bob")); }
public List<UserAudit> getAuditTrail(String userId) {
return List.of(new UserAudit(userId, "LOGIN", 1000L));
}
public AnalyticsReport getAnalytics(String query) { return new AnalyticsReport("results"); }
}
// --- Clients depend only on what they need ---
class UserAuthService {
private final UserReader reader;
public UserAuthService(UserReader reader) { this.reader = reader; }
public User authenticate(String id) { return reader.findById(id); }
}
class UserRegistrationService {
private final UserWriter writer;
public UserRegistrationService(UserWriter writer) { this.writer = writer; }
public void register(User user) { writer.save(user); }
}
class UserAdminService {
private final UserAuditReader auditReader;
public UserAdminService(UserAuditReader auditReader) { this.auditReader = auditReader; }
public void showAudit(String userId) {
auditReader.getAuditTrail(userId).forEach(a -> System.out.println(" Audit: " + a));
}
}
// Utility classes for demo
record User(String id, String name) {}
record UserAudit(String userId, String action, long timestamp) {}
record AnalyticsReport(String data) {}
public class Main {
public static void main(String[] args) {
PostgresUserRepository repo = new PostgresUserRepository();
UserAuthService auth = new UserAuthService(repo);
auth.authenticate("42");
UserRegistrationService reg = new UserRegistrationService(repo);
reg.register(new User("99", "bob"));
UserAdminService admin = new UserAdminService(repo);
admin.showAudit("42");
}
}Review
Help Us Improve
How helpful was this walkthrough?
Click a star to rate. We actively use this feedback to refine and update our system design content.
Placeholder
Discussion
Share your thoughts, ask questions, or help others.
Loading comments...