views:

62

answers:

2

Hi,

I am getting back again and again to it thinking about the best way to perform validation on POCO objects that need access to some context (ISession in NH, IRepository for example).

The only option I still can see is to use Service Locator, so my validation would look like:

public User : ICanValidate {
    public User() {} // We need this constructor (so no context known)

    public virtual string Username { get; set; }

    public IEnumerable<ValidationError> Validate() {
        if (ServiceLocator.GetService<IUserRepository>().FindUserByUsername(Username) != null)
            yield return new ValidationError("Username", "User already exists.")
    }
}

I already use Inversion Of control and Dependency Injection and really don't like the ServiceLocator due to number of facts:

  • Harder to maintain implicit dependencies.
  • Harder to test the code.
  • Potential threading issues.
  • Explicit dependency only on the ServiceLocator.
  • The code becomes harder to understand.
  • Need to register the ServiceLocator interfaces during the testing.

But on the other side, with plain POCO objects, I do not see any other way of performing the validation like above without ServiceLocator and only using IoC/DI.

Currently I perform such kind of validation in the service layer. So whenever an actor tries to change username (might be of course something different) the service performs this validation. One obvious disadvantage is that every service using the User must perform this check (even if it is one call).

So the question would be: is there any way to use DI/IoC for the situation described above?

Thanks,
Dmitriy.

+1  A: 

Repositories are generally at a higher level of abstraction than the domain objects they fetch/store. If you find your domain objects depending on repositories, then it's an indication of design problems upstream.

What you actually have is a circular dependency. The IUserRepository depends on the User and the User depends on the IUserRepository. This technically works, in the sense that it will compile if both objects are in the same assembly, but it will get you into trouble as a general design. There may be all sorts of objects that want to deal with a User but don't know anything about the IUserRepository it came from.

My suggestion to you is not to make this a "validation" property of the User. The validation should be performed by the repository itself, or - better yet - just have the repository raise an exception if the user name already exists when it tries to save.

There's a secondary reason for this suggestion. That reason is concurrency. Even if you validate the user name and find that it does not already exist, that may not be true 1 second later when you try to save that user. So you need to handle the exceptional case (tried to insert a user name that already exists) anyway. Given this, you might as well defer that until the last possible moment, since you have no way to make guarantees beforehand.

Domain objects should have no dependencies; if they self-validate, then the validation should depend only on the actual object being validated, and not other data in the database. The duplicate username constraint is actually a data constraint, not a domain constraint.

Summary: Move this particular validation outside the User class. It doesn't belong there; that's why you're finding yourself using this particular anti-pattern.

Aaronaught
I totally agree on that. Currently I perform the `Unique` validation in my service layer, but I have to duplicate/inherit the logic in each service that works with user.
Dmytrii Nagirniak
@Dmitriy: That's fine, and understandable; I would then put this logic in the *service* that the client/user interacts with, as opposed to the specific item. Delegate to a special validator if you need to keep the service tidy.
Aaronaught
All my `services` are currently the implementations of the perticular use-case/stories (so they can be seen as ones that user interacts with).What I will do is probably add another dependency `IUserValidation` and will inject it into the Use-Case service. I think it would be the best way to go for now.
Dmytrii Nagirniak
+1  A: 

Just to add what Aaronaught is saying. There is a larger problem with this design as the Domain Model validation should only validate attributes intrinsic to the model - not within the context of the larger system. Some examples of such intrinsic properties would be requirements for username length, acceptable characters, that both first and last name are filed etc.

The validation that you are performing is system-wide validation and belongs in a service/repository. This is how this system would look like if designed using Domain Driven Design:

public class User : ICanValidate {
    public User() {} 

    public virtual string Username { get; set; }

    public IEnumerable<ValidationError> Validate() {
        if (!string.IsNullOrEmpty(this.UserName))
          yield return new ValidationError("Username must not be empty");
    }
}

public class UserRepository : IUserRepository {
}

public static class UserService { 
  readonly IUserRepository Repository;

  static UserService() {
    this.Repository = ServiceLocator.GetService<IUserRepository>();
  }

  public static IEnumerable<ValidationError> Validate(User user) {
      if (Repository.FindUserByUsername(user.Username) != null)
          yield return new ValidationError("Username", "User already exists.")
  }
}
Igor Zevaka
Igor, thanks. I just added a user validation service so I can reuse it. I will consider it naming 'UserService' later one :)
Dmytrii Nagirniak