Search code examples
validationdomain-driven-designddd-repositoriesddd-service

DDD - Entity's repository dependent validations


I'm having a hard time trying to figure out the best way to implement business rules validation that depend on data stored in the database. In the simplified example bellow, I want to ensure the Username attribute is unique.

public class User() {
    public int Id { get; set; }
    public string Name { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    public string GenerateRandomPassword() {

    }
}

public interface IUserRepository : IRepository<User>
{
    bool UsernameTaken(string username);
}

public interface IUnitOfWork : IDisposable
{
    void Commit();
    IUserRepository Users { get; }
}

I've already read a lot of stuff about different ways of achieving that, which include injecting repository into entity (and prevent it gets in an invalid state), creating an extension method, etc.

However I don't think any of these are the best approach to do that.

So I decided to use an application service to orchestrate the entity validation using specifications.

public class CreateUserService : ICreateUserService
{
    private readonly IUnitOfWork _uow;

    public CreateUserService(IUnitOfWork uow)
    {
        _uow = uow;
    }

    public User Create(User user)
    {
        var usernameAvailableSpecification = new UsernameAvailableSpecification(_uow.Users);

        if (!usernameAvailableSpecification.IsSatisfiedBy(user))
        {
            throw new ValidationException("Username already taken");
        }

        user.GenerateRandomPassword();

        _uow.Users.Store(user);
        _uow.Commit();

        return user;
    }
}

At first, it seemed nice. But it's kind of hard to unit test, because the service is tightly coupled to the specification implementation and it has to deal with specification's dependencies manually. I thought about abstracting the specification as well, but I'm not sure if I'm the right path.

There is also the possibility I've started wrong, because actually I'm learning DDD and still don't have clear in my mind which layer should be responsible for this kind of validation.

Any help would be appreciated.


Solution

  • This is almost how I would model it, except that I would make the CreateUserService a domain service (and handle the Unit of Work in a separate application service). With this approach, I would not be too afraid of the tight coupling between the domain service and the specification, because username uniqueness seems to be a business rule that originates in the actual business domain (here, the "User and Authentication" bounded context).

    If you are still afraid of tight coupling between the CreateUserService and the specification, you can indeed further abstract the specification. Seems to be legit, but remember the YAGNI principle.