0
votes

I have been reading a lot online/offline about where to put validation and business rules in general for domain driven design. What I could not understand is how can an entity provides methods that does validation and business rules without resorting to static methods or having a service? This is especially important for cases where the domain object does not need to be instantiate yet, but we need to validate a value that will eventually used to set the object's attribute.

I noticed blog postings such as http://lostechies.com/jimmybogard/2007/10/24/entity-validation-with-visitors-and-extension-methods/ relies on .NET's specific extension method, which is not available in programming languages such as Java. I personally don't like static methods are they cannot be overridden and hard to test.

Is there anyway I could do this without static methods or having to instantiate an unnecessary domain object just to use its validation and business rules methods. If not, does that mean domain driven design is very dependent on static methods?

Thanks

2
Could you post an example or two? - Yugang Zhou
Let's just take an example of a website registration. - user3054039
Let's just take an example of an e-commerce website registration. If registering a customer required the customer to specify a username for the website account. The customer will be notified by emails if the registration is successful (member details needs to be reviewed along with other information provided during the registration steps) Since according to DDD, the Customer entity (represented by Customer class) must contain the method to validate username, how would this method be called if the Customer object is not created until the registration is reviewed? - user3054039

2 Answers

2
votes

Use ValueObjects Not Entity.

In the registration case, a UserName value object could be introduced. Create a Username object when receiving the registration. Implement validation in the constructor of the UserName.

See this question and this presentation for more detail.

Edit1:
1.How to handle cases where different validation rules applied for different context.
For example: The username must not have numbers for certain type of members, but it is required for other types of members?

Maybe different factory methods could do that. like UserName.forGoldenCardMember(...) or UserName.forPlainMember(...). Or make MemberType (a hierachy maybe) to validate UserName.

Another alternative solution is use AggregateFactory(AccountFactory in this case).

2.Is constructor the only place to put the validation code? I did read online about two points of view: an object must always be valid vs. not always. Both present good arguments, but any other approach?

I prefer valid approach personally. Passing an maybe invalid value object harms encapsulabilty.

Edit2:
Require a) validation business rule based on context(different username rules for member types) b) keep validating all business rules even if one of them fail

Stick with Single responsibility principle by using Value Object(MemberType this case). AggregateFactory could be introduced to ease the application layer(coarser granularity).

class AccoutFactory {
    Account registerWith(Username username, MemberType type, ....) {
        List<String> errors = new ArrayList<String>();
        errors.addAll(type.listErrorsWith(username));
        errors.add(//other error report...

        if (CollectionUtils.isEmpty(errors)) {
            return new Account(username,....);
        } else {
            throw new CannotRegisterAccountException(errors);
        }
    }
}

Edit3: For questions in the comments
a) Shouldn't the Username object be the one that has a method that returns the error like the listErrorsWith()? After all, it is the username that has different rules for different member type?

We could check this question from another perspective: MemberTypes have different rules for username. This may replace if/else block in the Username.listErrosWith(String, MemeberType) with polymorphism;

b) If we have the method in the MemberType, the knowledge will not be encapsulated in the Username.Also, we are talking about making sure Username is always valid.

We could define the validity of Username without MemberType rules. Let’s say "[email protected]" is a valid username, it is a good candidate for GoldenCard member but not good for SilverCard member.

c) I still can't see how performing validation that returns a list of errors without getting the list from exception thrown by the constructor or static method. Both does not look ideal IMHO.

Yes, the signature of listErrorsWith():List looks weired, I'd rather use validate(username) with no returning value(throw exception when fails). But this will force the cilent to catch every validation step to run validations all at once.

1
votes

If you decided to use DDD in your application you need to build more complex solution. I agree with @Hippoom, you shouldn't use Entity for this purpose.

I would suggest this solution:

DTO -> Service Layer (ValidationService -> Converter) -> Persistence Layer (Repository)

Some explanation: When you received DTO from client side with all necessary parameters, you should validate it in you service layer (e.g. Use another service like ValidationService) which can throw exception if something wrong. If all Ok, you can create Entity from your DTO in Converter and persist it in Repository.

If you want flexible solution for ValidationService I'd suggest Drools