domain-driven-designdddd

Should Value Objects Contain Technical Validation For Input Parameters?


As DDD practitioner advise, business rule's validations must be implemented inside domain objects (Entities, Value Objects and Domain Services) and follow their own context also somewhere i've read that we should put technical validations (such as check length, correct input formats, correct data type, ...) out of domain model and somewhere like application layer to keep domain object clear.

Now my question is this:

If we had a Value Object for credit card number, should we still keep technical validation out of our Value Object? In other words the "Self Validated" term is not involved with technical validations when we deal with Value Objects?

When an incorrect debit card's number or even an email address has a potential to break business rules, what then?

For more clarity please notice this Value Object which represent an Debit Card Number:

    public class DebitCardNumber : ValueObject
{
    public string Number { get;private set; }

    public DebitCardNumber (string number)
    {
        Validation(number);

        this.Number = number;
    }

    private void Validation(string number)
    {
        if (String.IsNullOrWhiteSpace(number))
        {
            throw new CardNumberCanNotBeEmptyException();
        }

        if (number.Length != 16)
        {
            throw new CardNumberLengthMustBeSixteenDigitException();
        }

        int sum = 0;
        for (int i = 1; i <= 16; i++)
        {
            if (!char.IsDigit(number[i - 1]))
            {
                throw new ValueContainsSomeNonDigitCharacterException();
            }

            int m = (i % 2 == 0 ? 1 : 2);
            int a = (int.Parse(number[i - 1].ToString()) * m);

            while (a > 9)
            {
                a -= 9;
            }

            sum += a;
        }

        if ((sum % 10) > 0)
        {
            throw new ValueIsNotCorrectAsACardNumberException() 
                { Message = "Perhaps some digits has been entered in wrong order or they are incorrect." };

        }
    }
}

According to this code there is a Validation method that carry out an algorithm to find out either is it Card Number's format correct or not? do you think is it right place for this type of validations?


Solution

  • as DDD practitioner advise, business rule's validations must be implemented inside domain objects (Entities,Value Objects and Domain Services)

    Yes.

    also somewhere I'd read that we should put technical validations (such as check length,correct input formats,correct data type,...) out of domain model and somewhere like application layer to keep domain object clear.

    A little bit confused here; the main point is that the entities shouldn't need to be worrying about a bunch of input validation, that isn't their job (separation of responsibilities). So instead of passing raw data (strings, primitives) to the entities in our model, we first use the primitives to construct value types that the entity will recognize, and then pass those entities in.

    The rules for which primitives can be used to create a well formed value type are properly implemented within the value type itself (constructor) or in a dedicated factory provided for that purpose). The application component has the responsibility to create the value type from the message/DTO it has received before passing that value to the model.

    So in your example, the DebitCard validation logic looks like it is in the right place.

    A caution - your model evolves over time; when the model changes, you'll still need to be able to read the data written by the earlier version of your model. Adding validation rules that treat your current data as invalid can get messy - so you want to make sure that the validation rules have business motivation. Are you saving money/cutting costs by ensuring that a debit card number has a valid checksum?

    (Example: suppose a customer submits a purchase order with an invalid card number. Does the business want to reject that order, or accept that order but defer acting on it until a valid form of payment is provided? If it's the latter choice, you want to make sure that your validation logic doesn't get in the way of accepting the order).