I have an immutable User entity:
public class User {
final LocalDate lastPasswordChangeDate;
// final id, name, email, etc.
}
I need to add a method that will return information if the user's password must be changed i.d. it has not been changed for more than the passwordValidIntervalInDays system setting.
The current approach:
public class UserPasswordService {
private SettingsRepository settingsRepository;
@Inject
public UserPasswordService(SettingsRepository settingsRepository) {
this.settingsRepository = settingsRepository;
}
public boolean passwordMustBeChanged(User user) {
return user.lastPasswordChangeDate.plusDays(
settingsRepository.get().passwordValidIntervalInDays
).isBefore(LocalDate.now());
}
}
The question is how to make the above code more object oriented and avoid the anemic domain model antipattern? Should the passwordMustBeChanged method be moved to User if so how to access SettingsRepository, should it be injected into User's constructor, or should a Settings instance be provided to the ctor, or should the passwordMustBeChanged method require a Settings instance to be provided?
The code of Settings and SettingsRepository is not important, but for completness, here it is:
public class Settings {
int passwordValidIntervalInDays;
public Settings(int passwordValidIntervalInDays) {
this.passwordValidIntervalInDays = passwordValidIntervalInDays;
}
}
public class SettingsRepository {
public Settings get() {
// load the settings from the persistent storage
return new Settings(10);
}
}
For a system-wide password expiration policy your approach is not that bad, as long as your
UserPasswordServiceis a domain service, not an application service. Embedding the password expiration policy within User would be a violation of the SRP IMHO, which is not much better.You could also consider something like (where the factory was initialized with the correct settings):
If eventually the policy can be specified for individual users then you can simply store policy objects on
User.You could also make use of the ISP with you current design and implement a
PasswordExpirationPolicyinterface on yourUserPasswordServiceservice. That will give you the flexibility of refactoring into real policy objects later on without having to change how theUserinteracts with the policy.If you had a
Passwordvalue object you may also make things slightly more cohesive, by having something like (the password creation date would be embedded in the password VO):