Where to put global rules validation in DDD
Asked Answered
M

10

75

I'm new to DDD, and I'm trying to apply it in real life. There is no questions about such validation logic, as null check, empty strings check, etc - that goes directly to entity constructor/property. But where to put validation of some global rules like 'Unique user name'?

So, we have entity User

public class User : IAggregateRoot
{
   private string _name;

   public string Name
   {
      get { return _name; }
      set { _name = value; }
   }

   // other data and behavior
}

And repository for users

public interface IUserRepository : IRepository<User>
{
   User FindByName(string name);
}

Options are:

  1. Inject repository to entity
  2. Inject repository to factory
  3. Create operation on domain service
  4. ???

And each option more detailed:

1 .Inject repository to entity

I can query repository in entities constructor/property. But I think that keeping reference to repository in entity is a bad smell.

public User(IUserRepository repository)
{
    _repository = repository;
}

public string Name
{
    get { return _name; }
    set 
    {
       if (_repository.FindByName(value) != null)
          throw new UserAlreadyExistsException();

       _name = value; 
    }
}

Update: We can use DI to hide dependency between User and IUserRepository via Specification object.

2. Inject repository to factory

I can put this verification logic in UserFactory. But what if we want to change name of already existing user?

3. Create operation on domain service

I can create domain service for creating and editing users. But someone can directly edit name of user without calling that service...

public class AdministrationService
{
    private IUserRepository _userRepository;

    public AdministrationService(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public void RenameUser(string oldName, string newName)
    {
        if (_userRepository.FindByName(newName) != null)
            throw new UserAlreadyExistException();

        User user = _userRepository.FindByName(oldName);
        user.Name = newName;
        _userRepository.Save(user);
    }
}

4. ???

Where do you put global validation logic for entities?

Thanks!

Mclin answered 28/4, 2011 at 12:52 Comment(0)
F
65

Most of the times it is best to place these kind of rules in Specification objects. You can place these Specifications in your domain packages, so anybody using your domain package has access to them. Using a specification, you can bundle your business rules with your entities, without creating difficult-to-read entities with undesired dependencies on services and repositories. If needed, you can inject dependencies on services or repositories into a specification.

Depending on the context, you can build different validators using the specification objects.

Main concern of entities should be keeping track of business state - that's enough of a responsibility and they shouldn't be concerned with validation.

Example

public class User
{
    public string Id { get; set; }
    public string Name { get; set; }
}

Two specifications:

public class IdNotEmptySpecification : ISpecification<User>
{
    public bool IsSatisfiedBy(User subject)
    {
        return !string.IsNullOrEmpty(subject.Id);
    }
}


public class NameNotTakenSpecification : ISpecification<User>
{
    // omitted code to set service; better use DI
    private Service.IUserNameService UserNameService { get; set; } 

    public bool IsSatisfiedBy(User subject)
    {
        return UserNameService.NameIsAvailable(subject.Name);
    }
}

And a validator:

public class UserPersistenceValidator : IValidator<User>
{
    private readonly IList<ISpecification<User>> Rules =
        new List<ISpecification<User>>
            {
                new IdNotEmptySpecification(),
                new NameNotEmptySpecification(),
                new NameNotTakenSpecification()
                // and more ... better use DI to fill this list
            };

    public bool IsValid(User entity)
    {
        return BrokenRules(entity).Count() == 0;
    }

    public IEnumerable<string> BrokenRules(User entity)
    {
        return Rules.Where(rule => !rule.IsSatisfiedBy(entity))
                    .Select(rule => GetMessageForBrokenRule(rule));
    }

    // ...
}

For completeness, the interfaces:

public interface IValidator<T>
{
    bool IsValid(T entity);
    IEnumerable<string> BrokenRules(T entity);
}

public interface ISpecification<T>
{
    bool IsSatisfiedBy(T subject);
}

Notes

I think Vijay Patel's earlier answer is in the right direction, but I feel it's a bit off. He suggests that the user entity depends on the specification, where I belief that this should be the other way around. This way, you can let the specification depend on services, repositories and context in general, without making your entity depend on them through a specification dependency.

References

A related question with a good answer with example: Validation in a Domain Driven Design.

Eric Evans describes the use of the specification pattern for validation, selection and object construction in chapter 9, pp 145.

This article on the specification pattern with an application in .Net might be of interest to you.

Foltz answered 28/4, 2011 at 12:52 Comment(17)
If the entity doesn't depend on the specification, how do you enforce that the entity satisfies the specification?Heighho
You don't - whether an entity is "valid" depends on its state and its context. George Polevoy points this out nicely in his answer and I share his opinion.Foltz
Ah, I'm starting to see the bigger picture now, interesting. I hope George (or you) follows up on his answer to explain what it would look like in code.Heighho
As per request - added a small example.Foltz
@Marijn, Thanks for links. But I'm completely with Greg Young here: "Don't ever let your entities get into an invalid state". I think it's better not to have any invalid User objects, then to forget validate one User...Mclin
I consider validation and preventing your object get into an invalid state to be two different things. Invalid state that an entity should prevent, should be determined from the information encapsulated by the entity only. When external information is needed (such as UserNameIsTaken) it should not be encapsulated by the entity. But this my opinion ... it is interesting to read the different views on this.Foltz
What I found interesting, is that the other answers promote the use of specification objects, but let the entity depend on the specification instead of the other way around. IMO this might result in undesired dependencies for the entity and unexpected performance hits (like calling a remote service from a property setter). These views are all expressed in this thread, which makes this an interesting question.Foltz
While this gives you the flexibility of swapping specs for validation, it just isn't practical to allocate a specification object for simple property validation. With hundreds of domain objects, this would literally take up to 10/20 spec classes to validate a simple object. Great in theory, terrible in practice.Cheeky
@Sergey - the original question was on validation logic other than simple "constructor/property" validation, such as validation rules spanning multiple entities. I added the the simple "not empty" specification as an example on how to combine multiple specifications.Foltz
@Sergey - that being said, I do like having a validation framework like this in place for my viewmodels. But applying validation like "not empty" should be as simple as applying an attribute or changing validation configuration. When validating I would like to know exactly which validation rules didn't pass, so the framework has to facilitate that. This also has to work for simple property validation.Foltz
@Foltz Thanks for replying! Now that I look at it again, I do agree that this pattern could work pretty well for complex business rule implementations. And a single implementation of IValidator w/o specs would provide a simple property validation on the entire object. I would probably create a couple of base classes to support all of the plumbing for simple validator and Spec-based validator, if i were to use this. So, this is a bit more flexible than I thought. Hey, +1 for the answer. :)Cheeky
@Foltz What happens when your Validator for an action , depends on multiple objects. For example, I have a bidAuction action and my Validator need to check some rules about auction object and some rules about the price object. How these values are aplied to the rules that are "specified" in the business rules specification?Succinate
You can inject all the required objects into the specification. You can even opt to inject other specifications. But imo it is a smell if it gets complicatedFoltz
I like this Specification pattern, but I'm still a bit unsure as to where this validation fits in the rest of the model. What component is responsible for creating and using the UserPersistenceValidator in the example above? An application service? A domain service?Theran
@Theran same question hereChantal
@Foltz Do you offer to add services (or repository) dependencies to specification? I mean service Service.IUserNameService which looks like 3rd party which may make some even http request.Arytenoid
Yes some specifications might require such a dependency.Foltz
C
13

I would not recommend disallowing to change properties in entity, if it's a user input. For example, if validation did not pass, you can still use the instance to display it in user interface with validation results, allowing user to correct the error.

Jimmy Nilsson in his "Applying Domain-Driven Design and Patterns" recommends to validate for a particular operation, not just for persisting. While an entity could be successfully persisted, the real validation occurs when an entity is about to change it's state, for example 'Ordered' state changes to 'Purchased'.

While creating, the instance must be valid-for-saving, which involves checking for uniqueness. It's different from valid-for-ordering, where not only uniqueness must be checked, but also, for example, creditability of a client, and availability at the store.

So, validation logic should not be invoked on a property assignments, it should be invoked upon aggregate level operations, whether they are persistent or not.

Colwen answered 28/4, 2011 at 18:29 Comment(8)
Good point, but where should OP put his 'global validation' code?Foltz
I think it's the view-model's job to hold the user input, not the entity. The aggregate-level operation would then be the copying of the view-model to the entity.Heighho
@Foltz I think the main point is that there is no 'global validation code'. Instead the validation manifests in different parts of the domain model. E.g. checking the input if it's valid -> view model. Checking for uniqe instances -> model/entity. Imho the validation concern can be sliced acros the domain as any other concernLui
I get the point (and agree to it), but I find that it does not really answer the question. @George Polevoy, how would you code your validation logic and where would you put it?Foltz
@George Polevoy, definitely we should check local invariants on aggregate level. But where to put global invariants check? @Niels van der Rest, I don't think view-model is a good place for business rules validationMclin
There is no such thing as 'the very global'. Some context might be 'more global then the other' but it does not make it outstanding in anyway. One point is that validation goes along with responsibility. View/Model is responsible to inform user about the mistake asap, so it must have it's own Interpreter for the set of rules involved, even if it does not decide if operation should be carried out or rejected. Backend service might validate against the same set of rules. This leads to an interpretable rule design, but it does not help in deciding on "where to put the code?"Colwen
@Marijn, It's not about 'put validation logic outside the entity', it's about not disallowing to change properties. You can set properties, then check if an invariant is held.Colwen
You're right, thanks for pointing it out (again); removed my earlier comment.Foltz
H
9

Edit: Judging from the other answers, the correct name for such a 'domain service' is specification. I've updated my answer to reflect this, including a more detailed code sample.

I'd go with option 3; create a domain service specification which encapsulates the actual logic that performs the validation. For example, the specification initially calls a repository, but you could replace it with a web service call at a later stage. Having all that logic behind an abstract specification will keep the overall design more flexible.

To prevent someone from editing the name without validating it, make the specification a required aspect of editing the name. You can achieve this by changing the API of your entity to something like this:

public class User
{
    public string Name { get; private set; }

    public void SetName(string name, ISpecification<User, string> specification)
    {
        // Insert basic null validation here.

        if (!specification.IsSatisfiedBy(this, name))
        {
            // Throw some validation exception.
        }

        this.Name = name;
    }
}

public interface ISpecification<TType, TValue>
{
    bool IsSatisfiedBy(TType obj, TValue value);
}

public class UniqueUserNameSpecification : ISpecification<User, string>
{
    private IUserRepository repository;

    public UniqueUserNameSpecification(IUserRepository repository)
    {
        this.repository = repository;
    }

    public bool IsSatisfiedBy(User obj, string value)
    {
        if (value == obj.Name)
        {
            return true;
        }

        // Use this.repository for further validation of the name.
    }
}

Your calling code would look something like this:

var userRepository = IoC.Resolve<IUserRepository>();
var specification = new UniqueUserNameSpecification(userRepository);

user.SetName("John", specification);

And of course, you can mock ISpecification in your unit tests for easier testing.

Heighho answered 28/4, 2011 at 16:33 Comment(10)
So, calling code should have access to repository? Thus caller should be domain or application service. So, what the benefit from injecting specification to user entity, if calling code could just call userRepository.IsNameUnique("John")?Mclin
@lazyberezovsky: By making the specification part of the signature of SetName, the calling code can immediately see that the name has to follow a certain specification. If you depend on the 'outer' code to call userRepository.IsNameUnique("John"), you can't ensure that the name is actually unique, because the outer code can easily omit the call the the repository.Heighho
I really like this approach! It's the perfect blend of being explicit enough to let consuming code understand the dependency on UniqueUserNameSpecification when called User.SetName, the ability to mock out the specification, and "safe" enough to know the entity is still taking care of the validation of username without relying on some other code/layer to call it. For example. relying on the Service Layer to call the specification during a .Create() method call. This way, your entity will never be allowed to get into an invalid state, which is the goal.Stephanestephani
One more quick comment though. When you go to create a User (via a constructor), there would be no way to pass in the IUserRepository to pass to the CreateUser method. Passing in Repositories to Entities in the constructor seems like a "bad thing" to do, so in theory, this enity could be created in an invalid state. The only thing I could think to do would be to allow the User entity to be created with a blank name property, and after the User constructor is called, the make consuming code use the SetName method to assign a name to the User.Stephanestephani
@indiecodemonkey That sounds like a good approach. Additionally, you can encapsulate all of the creation logic in a builder class that doesn't expose the 'blank' instance until it is in a completely valid state.Heighho
@Neils van der Rest: so basically, a simple factory?Stephanestephani
@indiecodemonkey There are a few differences between the Factory and Builder pattern, but yes, that is the general idea :)Heighho
I wouldn't inject the Repo into the Specification. I would pass whatever data you need to Satisfy your specification with. In the constructor, I would Guard that the data I need to validate against is actually passed, otherwise I would throw an exception. That way I am not 'indirectly' injecting the Repo into the Domain, which is a NO NO in DDD.Eschew
What about concurrency issues? Practically, each (almost) any client-server HTTP-based architecture is inherently concurrent. Specification can be fulfilled by two concurrently running operations, which are "a bit long", and until it comes to persist/save, then these specifications might no longer be valid... So, how to handle that?Circumfluous
@kravemir, in most cases optimistic concurrency is sufficient, where you assume the persistence will succeed once the specifications are fulfilled. As you already mentioned, any distributed system is inherently concurrent and specifications can therefore never guarantee full consistency. That's not their purpose; they are meant to catch most invalid input and provide (user) feedback if it fails. The persistence layer is responsible for guaranteeing a consistent state e.g. by defining unique indices. In the rare case something falls through, handle the exception as graceful as possible.Heighho
S
3

I’m not an expert on DDD but I have asked myself the same questions and this is what I came up with: Validation logic should normally go into the constructor/factory and setters. This way you guarantee that you always have valid domain objects. But if the validation involves database queries that impact your performance, an efficient implementation requires a different design.

(1) Injecting Entities: Injecting entities can be technical difficult and also makes managing application performance very hard due to the fragmentation of you database logic. Seemingly simple operations can now have an unexpectedly performance impact. It also makes it impossible to optimize your domain object for operations on groups of the same kind of entities, you no longer can write a single group query, and instead you always have individual queries for each entity.

(2) Injecting repository: You should not put any business logic in repositories. Keep repositories simple and focused. They should act as if they were collections and only contain logic for adding, removing and finding objects (some even spinoff the find methods to other objects).

(3) Domain service This seems the most logical place to handle the validation that requires database querying. A good implementation would make the constructor/factory and setters involved package private, so that the entities can only be created / modified with the domain service.

Strive answered 28/4, 2011 at 18:59 Comment(3)
(1) I don't think performance will depend whether we make database call from constructor, or from caller - we need to make that call anyway. (2) I talked about injecting repository to factory. It looks good with immutable objects. But if global rule needs to be checked later in object's lifetime, we can't use factory. (3) Domain service is very good candidate for global logic. But I think domain service purpose is coordination of several objects interactions, so maybe it's also wrong place?Mclin
@lazyberezovsky: (1) My preference is to keep constructor’s simple and side effect free, otherwise use a factory. And when handling 100 entities it’s not always necessary to also do 100 individual database calls, often you optimize to fewer DB round trips when handling big sets of entities. By putting all logic in the entity you deprive yourself of the possibility to handle big sets of entities different from single entities. (2) True, but you can use factories, plus domain services.Strive
@lazyberezovsky: (3) DDD is about domain focus, we need to watch out for making it a dogma, so we should always design with the implementation technology and (performance) requirements in mind. BTW domain services seem also be a catch all phrase for all kind of patterns that can be used to implement a solution that coordinates operations on entities.Strive
Q
3

I would use a Specification to encapsulate the rule. You can then call when the UserName property is updated (or from anywhere else that might need it):

public class UniqueUserNameSpecification : ISpecification
{
  public bool IsSatisifiedBy(User user)
  {
     // Check if the username is unique here
  }
}

public class User
{
   string _Name;
   UniqueUserNameSpecification _UniqueUserNameSpecification;  // You decide how this is injected 

   public string Name
   {
      get { return _Name; }
      set
      {
        if (_UniqueUserNameSpecification.IsSatisifiedBy(this))
        {
           _Name = value;
        }
        else
        {
           // Execute your custom warning here
        }
      }
   }
}

It won't matter if another developer tries to modify User.Name directly, because the rule will always execute.

Find out more here

Quodlibet answered 29/4, 2011 at 8:22 Comment(5)
The downside of putting the validation in the setter is that it hides the dependency on the specification. Changing properties should also be a lightweight operation, which isn't the case if the specification invokes a database or web service.Heighho
There are many ways to skin a cat. The point was to highlight the Specification pattern, and show there's more to DDD than Services and Repositories. I fear that dumping too many methods inside Services or Repositories ultimately leads to a weaker domain model - one which is harder to communicate with a Domain Expert (the reason why DDD exists).Quodlibet
Of course, there's much more to DDD than just design patterns. But exposing the specification dependency seems like a good thing to me, because it clearly shows that the name isn't just any string. You might as well go the whole nine yards and introduce a UserName class. The stronger, the better :)Heighho
One issue with code above - you call IsSatisifiedBy(this) before changing name. So, it will be true always. You can set name first. But in that case you can leave User object in non-valid state.Mclin
What about concurrency issues? Practically, each (almost) any client-server HTTP-based architecture is inherently concurrent. Specification can be fulfilled by two concurrently running operations, which are "a bit long", and until it comes to persist/save, then these specifications might no longer be valid... So, how to handle that?Circumfluous
U
1

In my CQRS Framework, every Command Handler class also contains a ValidateCommand method, which then calls the appropriate business/validation logic in the Domain (mostly implemented as Entity methods or Entity static methods).

So the caller would do like so:

if (cmdService.ValidateCommand(myCommand) == ValidationResult.OK)
{
    // Now we can assume there will be no business reason to reject
    // the command
    cmdService.ExecuteCommand(myCommand); // Async
}

Every specialized Command Handler contains the wrapper logic, for instance:

public ValidationResult ValidateCommand(MakeCustomerGold command)
{
    var result = new ValidationResult();
    if (Customer.CanMakeGold(command.CustomerId))
    {
        // "OK" logic here
    } else {
        // "Not OK" logic here
    }
}

The ExecuteCommand method of the command handler will then call the ValidateCommand() again, so even if the client didn't bother, nothing will happen in the Domain that is not supposed to.

Unalloyed answered 5/5, 2011 at 12:33 Comment(0)
B
1

in short you have 4 options:

  • IsValid method: transition an entity to a state (potentially invalid) and ask it to validate itself.

  • Validation in application services.

  • TryExecute pattern.

  • Execute / CanExecute pattern.

read more here

Basra answered 14/1, 2021 at 19:11 Comment(0)
I
0

Create a method, for example, called IsUserNameValid() and make that accessible from everywhere. I would put it in the user service myself. Doing this will not limit you when future changes arise. It keeps the validation code in one place (implementation), and other code that depends on it will not have to change if the validation changes You may find that you need to call this from multiple places later on, such as the ui for visual indication without having to resort to exception handling. The service layer for correct operations, and the repository (cache, db, etc.) layer to ensure that stored items are valid.

Insufflate answered 7/5, 2011 at 2:7 Comment(2)
So, you suggest to use option (3), but with separate validation method?Mclin
yes, just call the validation method from inside of your create user method. if it returns false, throw the exception.Insufflate
V
0

I like option 3. Simplest implementation could look so:

public interface IUser
{
    string Name { get; }
    bool IsNew { get; }
}

public class User : IUser
{
    public string Name { get; private set; }
    public bool IsNew { get; private set; }
}

public class UserService : IUserService
{
    public void ValidateUser(IUser user)
    {
        var repository = RepositoryFactory.GetUserRepository(); // use IoC if needed

        if (user.IsNew && repository.UserExists(user.Name))
            throw new ValidationException("Username already exists");
    }
}
Volt answered 8/5, 2011 at 11:23 Comment(0)
A
-3

Create domain service

Or I can create domain service for creating and editing users. But someone can directly edit name of user without calling that service...

If you properly designed your entities this should not be an issue.

Aldrin answered 28/4, 2011 at 14:50 Comment(3)
Design is simple - just property Name.Mclin
This is what i would have said but perhaps not as.. "plainly" :DEngland
-1 I don't think this is a helpful answer. I don´t understand why it was upvoted,Foltz

© 2022 - 2024 — McMap. All rights reserved.