// SOLID & Principles · 04
Interface Segregation
Clients shouldn't depend on methods they don't use.
How it fits together
- ProfilePage uses / calls UserApi (Read(id))
- ReadOnlyUserCache implements UserApi
- UserService implements UserReader
- UserService implements UserWriter
- ProfilePage uses / calls UserReader (Read / Search)
- AdminTool uses / calls UserWriter
Spot it
- Implementations that throw "not implemented"
- A read-only consumer can also delete
- Test fakes stub ten methods to use one
It costs
More interfaces to name and register; split too far and you get one-method noise.
In code
public interface IUserReader{ Task<User?> ReadAsync(Guid id, CancellationToken ct); Task<IReadOnlyList<User>> SearchAsync(string query, CancellationToken ct);}public interface IUserWriter{ Task CreateAsync(UserDto dto, CancellationToken ct); Task DeleteAsync(Guid id, CancellationToken ct);}public sealed class UserService(UserStore store) : IUserReader, IUserWriter{ public Task<User?> ReadAsync(Guid id, CancellationToken ct) => store.FindAsync(id, ct); public Task<IReadOnlyList<User>> SearchAsync(string query, CancellationToken ct) => store.SearchAsync(query, ct); public Task CreateAsync(UserDto dto, CancellationToken ct) => store.InsertAsync(dto, ct); public Task DeleteAsync(Guid id, CancellationToken ct) => store.RemoveAsync(id, ct);}public sealed class ProfilePage(IUserReader users){ public Task<User?> LoadAsync(Guid id, CancellationToken ct) => users.ReadAsync(id, ct);}Go deeperThe full explanation and what to ask for · 1 min readExpandCollapse
The interface belongs to the client, not the implementer. Shape it around what a caller needs; one class can still implement several such roles.
It is not "every interface has one method". The test is whether clients using different parts of the interface change for different reasons.
Fat interfaces leak: they couple consumers to changes they do not care about and push implementers into throwing for methods that make no sense for them.
Ask for it
- role interface
- client-specific interface
- fat interface
- cohesion
- interface splitting
Split the user service interface into role interfaces based on what each caller actually uses, e.g. UserReader and UserWriter. Keep one class implementing both, register each interface in the DI container against it, and make consumers depend on the narrowest one.