2

I have a question regarding validations and exceptions in DDD.

I have a ValueObject say, PasswordText which takes a string argument in it's constructor. Checks if the string matches the password criteria. If criteria, matches then the new object is initialized otherwise it throws an exception (DomainException).

The problem with this approach is, now, there could be many exceptions lying around in the Domain models that are ready to explode. It will be a headache for consumers of my Domain models to catch appropriate exceptions and act accordingly (like sending correct HTTP Status Code or something like that.)

My first question is how can I make this easy on them? Do I need to change my approach like using Notification Pattern or Either Pattern making it explicit for my consumers that this could return an error?

If the answer to above question is yes then how can I implement this approach for constructors? If answer of this question is Factories then sadly I have to create Factory for each domain model just for the sake of returning errors!

Also, I would like to add that the problem with Notification/Either Pattern is that, now my application code will be mixed with error handling code. How one should deal with this?

Is there any better way altogether for validations? Or How would you go about it?

8
  • Who are your "consumers"? Will they be in the same project, i.e. are you working on a part of an application? Are you working on a library or an application? If it is a library, will it be published on github, etc. (i.e. are you expecting unknown people and use-cases), or is it an internal one used by a predefined set of applications? Jul 12, 2021 at 9:11
  • How about creating a custom annotation for validating your object?
    – Chris Neve
    Jul 12, 2021 at 9:51
  • @RobertBräutigam No, only internal people will be using it. More specifically I'll be working on it and also consuming it. Even so, remembering all of the exceptions doesn't seem right. Jul 12, 2021 at 10:03
  • @ChrisNeve Yes, but the thing is what happens when validation fails? Jul 12, 2021 at 10:07
  • There is nothing wrong with throwing domain exceptions. You seem to have a grasp on your two options: burden the client with handling some sort of Either/Maybe at every domain call, or burden the client with wrapping calls in try/catch (or a single top-level handler). The latter is preferable in most systems (you would certainly know if you are working within a system that prefers the former). Many web frameworks already work by using a top-level try/catch to convert errors into HTTP messages/error pages. Jul 13, 2021 at 18:56

2 Answers 2

3

You're right. You shouldn't offload responsibilities to the consumer for no reason.

So if you are inside one app, and it seems you are, there is absolutely no reason to abstract away parts of the application. If you know you'll have to return a HTTP error, do that. Yes, from the "domain" objects.

Purists will probably argue that domain and web must not be mixed. But let's be pragmatic about this. Why maintain (for a lot of additional work and ongoing maintenance) an abstraction that you're not going to be using?

The object should be responsible for validating itself, and if it is a part of a web-application, should be capable of indicating what's wrong to the user through the web interface if it needs to.

Instead of a Factory, I would probably use a factory method though. Separate factory objects are more useful if you need to use factories polymorphically.

2

Is there any better way altogether for validations?

TL;DR: Parse, Don't Validate.


... takes a string argument in it's constructor. Checks if the string matches...

This is a classic parsing example: you are starting with information in a general purpose data structure (a string), and trying to represent that information more specifically (a domain value).

As a rule, the logic to do this lives outside of your domain model.

It lives outside of your model for the same sort of reason that you are considering throwing an exception - we don't want to pollute the domain logic with a bunch of rarely used plumbing code.

One reason that this is confusing: we often see what look to be "validation checks" in the constructors/initializers of our value objects. That constructor code is usually an expression of a defensive programming guard: trying to ensure that the value can satisfy its contract by ensuring that its own inputs are within an acceptable range.

In other words, the checks in your value object constructors aren't there to mitigate against invalid inputs (that's a parser's job), they are there to mitigate against bugs in the parser.


Either pattern is one mechanism for implementing a parser branch.

Either<CompliantPassword, NonCompliantPassword> e = parse(rawString)

Alternatively, you could return a callable/interface, with the actual target function determined by what kind of instance was returned. In other words, you could design the code so that the branching logic is within the parser, instead of within the code that receives the parsed message.

I use "message" deliberately here - in this context, we're really talking about the in memory representation of the input, which probably includes one or more interesting domain values, but is not necessarily itself a domain value, but rather an element in your application model.

5
  • Hi thanks, it was really amazing blog. It did gave me some insight which I am going to translate and you can correct me. So Using a Parser I can parse input to ValueObject (a datatype that closely represents my domain) and it should be done as above as I possibly could, in this way I can avoid checks further down the road. Right? Now where this should happen? Should I need to add like a ACL before my Application layer which does that and my Presentation/Controller/ Whatever layer communicates using ACL? Jul 12, 2021 at 17:09
  • @honey_ramgarhia That blog post is... questionable. The Maybe "monad" (a term not even cited in the post!) is indeed used to solve the class of problem you are asking about above, but the poster clearly does not understand how to work with the construct. The entire purpose of Maybe is to offload all of the "is there a value" checks away from the consumer in a way that allows them to work as if a value is always available... not what's happening in this post. There is little purpose of utilizing a monad if you are going to unpack it on the next line... Jul 13, 2021 at 18:17
  • 1
    @VoiceOfUnreason Similarly, I urge you to expand your example to include the next 3 LoC. I'm worried OP is going to go all wrong here and create a mess... There is a right way and a wrong way to continue from where you left off, and it should not look like the code in the linked blog post. I would expect the very next line to call a method (or maybe a overloaded operand) on e ! Not immediately unpack the value. I would also amend your example to utilize a different, more composable "failure" data type (e.g. Error list) to avoid the impending "type hell" that will occur. Jul 13, 2021 at 18:53
  • @king-side-slide Exceptions with a single top-level catch blocks are good for validating ‘object as a whole’ or ‘object composition’. But for ‘Properties/Attribute’ validations I think this blog makes a good point i.e. strengthening your arguments. Jul 14, 2021 at 9:36
  • @honey_ramgarhia I don't disagree with the idea of strengthening your arguments, but that doesn't really solve the problem does it? I call this kind of solution "moving the problem". All you will have accomplished is moving the validation into a different context where that result will then have to be handled. The problem is how do you get from a domain error (InvalidPasswordException or Either<CompliantPassword, NonCompliantPassword>) to an HTTP status code? You have to cross that bridge at some point. Utilizing Maybe is an option, but you end up in a local minimum if done wrong Jul 14, 2021 at 15:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.