Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DDD validation without throwing exceptions

I am attempting my first foray into DDD, and I asked a question about bulk imports here, but I am going in circles trying to apply the validation for my domain model.

Essentially I want to run through all the validation without throwing exceptions so that I can reject the command with all the validation errors via a list of CommandResult objects inside the Command object. Whilst some are just mandatory field checks which are configurable and so will be handled outside the aggregate, there are also business rules, so I don't want to duplicate the validation logic and don't want to fall into an anaemic model by moving everything outside the aggregate to maintain the always-valid mantra of entities.

I am at a bit of a loss, so thought it be best I ask the experts if this I am going about things correctly before I start muddying the waters further!

To try and demonstrate:

Take the below, we have fairly simple UserProfile aggregate, the constructor takes the minimum information required for a profile to exist.

 public class UserProfile : AggregateRoot
    {
        public Guid Id {get; private set; }
        public Name Name {get private set;}
        public CardDetail PaymentInformation {get; private set;}



      public UserProfile(Guid id, Name name, CardDetail paymentInformation)
        {
            Name = name;
            PaymentInformation = paymentInformation;
        }

    }

public class CardDetail : ValueObject
{
    public string Number {get; private set;}
    public string CVC {get; private set; }
    public DateTime? IssueDate {get; private set;}
    public DateTime ExpiryDate {get;private set;}

    public CardDetail(string number, string cvc, DateTime? issueDate, DateTime expiryDate)
    {
        if(!IsValidCardNumber(number))
        {
            /*Do something to say details invalid, but not throw exception, possibly?*/
        }
        Number = number;
        CVC = cvc;
        IssueDate = issueDate



        ExpiryDate = expiryDate;

    }

    private bool IsValidCardNumber(string number)
    {
        return Regex.IsMatch(/*regex for card number*/);
    }
}

I then have a method which accepts a command object, which will construct a UserProfile and save to the database, but I want to validate before saving

public void CreateProfile(CreateProfileCommand command)
{
    var paymentInformation = new CardDetail(command.CardNumber, command.CardCVC, command.CardIssueDate, command.CardExpiryDate)

    var errors = /* list of errors added to from card detail validation, possibly? */

    var profile = new UserProfile(/* pass args, add to errors? */

    if(errors.Any())
    {
        command.Results.Add(errors.Select(x => new CommandResult { Severity = Severity.Error, Message = x.Message });
        return;
    }

    /* no errors, so continue to save */

}

Now, I could handle exceptions and add them to the command result, but that seems expensive and surely violates the rule of allowing exceptions to control flow? but on the other hand I want to keep entities and value object valid, so I find myself in a bit of a rut!

Also, in the example above, the profile could be imported or done manually from a creation screen, but the user should get all error messages rather than each one in the order they occur. In the application I am working on, the rules applied are a bit more complex, but the idea is the same. I am aware that I shouldn't let a UI concern impact the domain as such, but I don't want to have to duplicate all validation twice more so that I can make sure the command won't fail as that will cause maintainability issues further down the line (the situation I find myself in and trying to resolve!)

like image 827
Steven Brookes Avatar asked Jun 19 '17 17:06

Steven Brookes


1 Answers

The question is maybe a bit broad and around architectural design which is something you should decide upon, but I will try and assist anyway -I just cannot help myself.

Firstly: This is a great article that might already hint at you are too critical about your design: http://jeffreypalermo.com/blog/the-fallacy-of-the-always-valid-entity/

You would need to decide about the way your system is going to handle validation.

That is, do you want a system where the domain will just absolutely never ever fail consistency? Then you might need additional classes to sanitize any commands as you have and validate them before you accept or reject the change to the domain(Sanitation layer). Alternatively, as in that article, it might indicate that there is a completely different type of object required to deal with a specific case. (something like legacy data which does not conform to current rules)

Is it acceptable for the domain to throw an exception when something seriously goes wrong? Then discard all changes in the current aggregate (or even current context) and notify the user.

If you are looking for a peaceful intermediate solution, maybe consider something like this:

public OperationResult UpdateAccount(IBankAccountValidator validator, IAccountUpdateCommand newAccountDetails)
    {
        var result = validator.Validate(newAccountDetails);
        if(result.HasErrors)
        {
            result.AddMessage("Could not update bank account", Severity.Error);
            return result;
        }

        //apply further logic here

        //return success
    }

Now you can have all the validation logic in a separate class, but you have to pass that and call via the double dispatch and you will add the result handling as seen above in every call. You will truly have to decide what style is acceptable for you/team and what will remain maintainable in the long run.

like image 182
Eugène Avatar answered Nov 13 '22 23:11

Eugène