6

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);
  }
}
Adam Siemion
  • 15,569
  • 7
  • 58
  • 92
  • I have read http://stackoverflow.com/questions/5694241/ddd-the-rule-that-entities-cant-access-repositories-directly and http://stackoverflow.com/questions/827670/is-it-ok-for-entities-to-access-repositories but none of them seem to answer my question – Adam Siemion Jun 12 '15 at 10:12
  • Take a look at [this](https://blog.inf.ed.ac.uk/sapm/2014/02/04/the-anaemic-domain-model-is-no-anti-pattern-its-a-solid-design/) – Henrique Barcelos Jun 12 '15 at 14:53

3 Answers3

4

For a system-wide password expiration policy your approach is not that bad, as long as your UserPasswordService is 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):

PasswordExpirationPolicy policy = passwordExpirationPolicyFactory().createDefault();
boolean mustChangePassword = user.passwordMustBeChanged(policy);


//class User
public boolean passwordMustBeChanged(PasswordExpirationPolicy policy) {
    return policy.hasExpired(currentDate, this.lastPasswordChangeDate);
}

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 PasswordExpirationPolicy interface on your UserPasswordService service. That will give you the flexibility of refactoring into real policy objects later on without having to change how the User interacts with the policy.

If you had a Password value object you may also make things slightly more cohesive, by having something like (the password creation date would be embedded in the password VO):

//class User
public boolean passwordMustBeChanged(PasswordExpirationPolicy policy) {
    return this.password.hasExpired(policy);
}
plalx
  • 42,889
  • 6
  • 74
  • 90
  • thanks, I have chosen the "your approach is not that bad" solution :) – Adam Siemion Jun 15 '15 at 14:48
  • 1
    @AdamSiemion For a more OOP design just implement a PasswordExpirationPolicy interface on your service and then you can do `user.passwordMustBeChanged(yourDomainService)`. You will have less friction if your design changes eventually and it's really not a huge refactor. – plalx Jun 15 '15 at 14:54
  • Create explicit predicate-like VALUE OBJECTS for specialized purposes. A SPECIFICATION is a predicate that determines if an object does or does not satisfy some criteria. – 王奕然 Aug 18 '15 at 02:32
  • @wangyiran That's pretty much what the `PasswordExpirationPolicy` is in my answer, no? – plalx Aug 18 '15 at 13:16
  • @plalx yes,i just refer the Eric Evans book reference – 王奕然 Aug 19 '15 at 04:02
1

just to throw out another possible solution would be to implement a long-running process that could do the expiration check and send a command to a PasswordExpiredHandler that could mark the user with having an expired password.

Marco
  • 2,453
  • 3
  • 25
  • 35
  • I just wanted to add that it's not an option that is mutually exclusive with what we proposed. Even with a long-running process, you need a clean way to check the policy. – plalx Jun 12 '15 at 19:24
  • @plax agreed. what's nice about the long-running process is you could bake other types of things within the workflow, like sending out a friendly reminder when their password is about to expire. – Marco Jun 12 '15 at 19:29
0

I have stumbled upon a document that provides an answer to my question:

A common problem in applying DDD is when an entity requires access to data in a repository or other gateway in order to carry out a business operation. One solution is to inject repository dependencies directly into the entity, however this is often frowned upon. One reason for this is because it requires the plain-old-(C#, Java, etc…) objects implementing entities to be part of an application dependency graph. Another reason is that is makes reasoning about the behavior of entities more difficult since the Single-Responsibility Principle is violated. A better solution is to have an application service retrieve the information required by an entity, effectively setting up the execution environment, and provide it to the entity.

http://gorodinski.com/blog/2012/04/14/services-in-domain-driven-design-ddd/

Adam Siemion
  • 15,569
  • 7
  • 58
  • 92