A small and simple example that shows one of the benefits of having domain objects.
Lets assume we need to make a request for some kind of a token and the flow for doing so goes like this:
- Validate an email address
- With the validated address validate a password
- With both the validated values request for the token
The not so revealing way
Q: Can the consumer of this API, without knowing the aforementioned flow, figure it out just by looking at the functions’ signatures?
A: I guess she could if the parameters’ names were like that. If not she needs to read the bodies of those functions to see that there is some kind of order that needs to be followed.
Q: Can the creator of this code be 100% certain that the functions will be used as expected and the passed parameters will be valid? For example, will requestToken always be called last and with valid addresses and passwords?
A: No! There is nothing that can guarantee that so, just to be safe, we check in every function that the provided values are valid and make the code flexible enough that, for example, each function could be used on its own. That will, potentially, lead to code duplication or unnecessary abstractions.
Q: What about errors and invalid values? Can the consumer of this API predict the code’s behavior on erroneous inputs without reading a documentation?
A: No. Just no.
The revealing way
First lets enrich our API with some, much needed, domain objects:
Having those constructs we can change the functions and make them reveal both the order they can be used and their behavior:
So lets pretend that we are the consumer of this API and all we have is the code.
Our main goal is to request for a token. By just looking at the functions’ signatures we see that there is a requestToken. Nice! Our task is half way done! What do we need for calling this function? A valid email address and a valid password. Ok. How do we get one of each?
Looking again at the signatures we see validateEmailAddress and validatePassword that return an EmailAddress and Password respectively. Lets check what those are. These are abstractions and each of them has been extended to a valid and an invalid state. The invalid one carries the error that occurred too! So, back to the functions.
We see that validating a password needs to be fed with a valid email address so we first need to call validateEmailAddress, then validatePassword and finally requestToken.
That’s it. We didn’t read the code of the functions and we didn’t have to worry about our inputs.