I am working on rewriting my ASP.NET MVC app using the domain driven design priciples. I am trying to validate my User entity. So far I am able to validate basic rules (like the username and password being a non null/whitespace string). However one of the rules, I need to make sure that the username is unique. However I need access to the database inorder to do this, which means I would have to inject my IUserRepository into my User entity like so.
public class User
{
private readonly IUserRepository _userRepository;
public User(IUserRepository repo)
{
_userRepository = repo;
}
public override void Validate()
{
//Basic validation code
if (string.IsNullOrEmpty(Username))
throw new ValidationException("Username can not be a null or whitespace characters");
if (string.IsNullOrEmpty(Password))
throw new ValidationException("Password can not be a null or whitespace characters");
//Complex validation code
var user = _userRepository.GetUserByUsername(Username);
if (user != null && user.id != id)
throw new ValidationException("Username must be unique")
}
}
However this seems … well wrong. Making my entity depend on my repository seems like a bad idea (correct me if I am wrong). But having the validation code in the entity makes sense. Where is the best place to put complex validation code?
In general, dependency on repository is not ‘wrong’, it is sometimes unavoidable. However I think it should be an exception and should be avoided as much as possible. In your scenario you may reconsider having this dependency. If you think about it, ‘uniqueness’ is not a responsibility of the entity itself because entity does not know about other entities. So why having entity enforce this rule?
I think that you may be overgeneralizing ‘validation’. I would get rid of the ‘Validate’ method and will make sure that the object does not get into ‘invalid’ state in the first place. I answered similar question few months ago.
Now back to the uniqueness rule. I think that this is one of the examples where DDD ‘leaks’ a bit, in a sense that the enforcement of this business rule can not be expressed purely in domain code. I would approach it like this:
The client code would know that before adding a new user, it should explicitly check for uniqueness, otherwise it will crash. This combination enforces business rule in the domain code, but it is usually not enough. As an additional enforcement layer you may want to add UNIQUE constraint in the database or employ explicit locking.