Reflecting business logic rules into the domain models using typescript — Part 2
In the previous article, we learned how to create self-documented types and models. but they were just types and didn't have any functionality.
In this article, we are going to write some code that creates and validates those types.
At the end of this article, our types can be used in real-life projects.
The project source code is available here:
Bootstrap
First, we need a typescript project. so let's create it.
yarn init -y
yarn add --dev typescript ts-node
./node_modules/.bin/tsc --init
Then we create our types.
String50
At the top, there is the type declaration. after that, there is the makeString50
function. this function accepts any
argument and tries to create String50
.
It checks if the argument type is a string
and its length is less than 50.
If the argument doesn't satisfy the rules and constraint it will throw a CustomTypeError
exception.
And here is CustomTypeError
It is just a simple class for showing errors.
We use it in try catch
blocks to handle our custom type errors.
Let's continue to define other types.
We used a regex to verify the argument is really an Email.
PostalCode
Just like the Email. using a regex to verify.
EmailAndPostalCode
First, we check if the argument is provided. then we use Email
and PostalCode
types to do the verifications.
ContactInfo
The ContactInfo
type is a union type. it could be Email
or PostalCode
or both of them.
in makeContactInfo
function, first we try to make an Email
, then try to make a PostalCode
and finally, we try to make EmailAndPostalCode
.
If all of them fail, then the exception will throw.
Now we have all types we want and can create the Person
model.
Person
In makePerson
function all we need to do is call the type maker functions.
If all of them return a validated value, we create our person model.
Let's create persons using makePerson
function and test what we created.
The createAndLogPerson
function only try to create a person and print the value. if it fails it will print the error.
Let's see the output:
- person 1 is valid and has email for contact info
- person 2 is valid and has postal code for contact info
- person 3 is valid and has both email and postal code for contact info
- person 4 is not valid because it does not have anything for contact info. so it will throw an error
- person 5 is not valid because it has the email for contact info, but it is not a correct email format. so it will throw an error
In this article, we make our types actually work and validate the input data.
But as you can see it is a boring job to do.
First, we need to declare the type and then we should write some function to make and validate it. And this type of structure for our models only makes sense in our domain and in our projects. What if we need to send this data to someone else. for example, what if we need to return this data as the response of a rest API call?
We need another function to get Person
as the argument and return a normal structure for it like:
{
"firstName": "pf1",
"lastName": "pl1",
"email": "pf1@gmail.com",
"postalCode": "3483848392",
}
We need to define something like encodePerson
function.
As you can we have to createmake...
and encode...
functions for every type.
We need some way to automate these functions.
Fortunately, there is a library for typescript to do just that. You can read about it here:
In the next article, we are going to create and validate our models using io-ts
library.