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?
2 Answers 2
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.
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.
-
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?honey_ramgarhia– honey_ramgarhia07/12/2021 17:09:58Commented Jul 12, 2021 at 17:09
-
1@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 ofMaybe
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...user3347715– user334771507/13/2021 18:17:42Commented Jul 13, 2021 at 18:17 -
2@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.user3347715– user334771507/13/2021 18:53:51Commented 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.honey_ramgarhia– honey_ramgarhia07/14/2021 09:36:15Commented 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
orEither<CompliantPassword, NonCompliantPassword>
) to an HTTP status code? You have to cross that bridge at some point. UtilizingMaybe
is an option, but you end up in a local minimum if done wronguser3347715– user334771507/14/2021 15:27:35Commented Jul 14, 2021 at 15:27
Explore related questions
See similar questions with these tags.
Either/Maybe
at every domain call, or burden the client with wrapping calls intry/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-leveltry/catch
to convert errors into HTTP messages/error pages.