What is considered valid?
Before we can test or design or recursive model, we need to ask the same question as all other domain models: ‘What is considered valid?’. Most domain modeling goes wrong because the initial reflection of ‘valid’ and ‘invalid’ is forgotten or ignored. In the beginning, it is easy to consider everything as ‘valid’, but as the project grows, you will see where things were invalid and then how bugs occur. These are cases that should have been handled before the model was tested or designed.
In the context of recursive types, the most obvious question is how deep and how wide the tree structure can go. The performance of the system will be tested if we play with these parameters, and rest assured that the property-based tests will stretch to their limit.
The limits of this exercise are defined in the Folder.Limits.maxDepth
and Folder.Limits.maxWidth
.
Testing the limits of 'valid'
Besides the depth and with of a recursive structure, there are other factors at play. Creating a recursive model includes matching children and parents together, and this is usually done with unique IDs. Because of this, several scenarios can occur:
- Folder with an ID the same as the parent ID, a.k.a. recursive input.
- Multiple folders with the same ID, a.k.a. duplicate input.
Together with the limits of our depth and width, we already have four test scenarios that will verify if our model is being constructed in the correct way.
Do not worry about specific implementations for now. The most important thing is that you understand that we now have a generative input that goes over all the test scenarios.
👀 Notice how the depth and width inputs are currently using hard-coded values to alter the size of the structure that is bigger than the defined limits; how the folder ID is set twice during duplicates in parent/child; and how the recursive folder is an invalid input with both the same ID of the folder ID and parent folder ID.
Guard against 'invalid'
The final part of the process is implementing the domain model itself. Creating recursive types is usually done via a flat list that has a parent/child relationship with ID’s. The domain model will not have a parent ID, but will be part of the tree structure. It will look something like this:
Creating such a structure from a flat list requires recursing, but also a validation step for each step in that recursive loop.
👀 Notice how the FPrimitive library can help in reusing validations with the dependsOn
function. The depth
function will calculate the current depth of the tree structure each time. The most important part is the recursive withChildren
function that builds up the recursive type.
Conclusion
Designing and testing recursive types in F# is an intuitive process, thanks to the language’s built-in support for recursion. When combined with validation libraries like FPrimitive, we can efficiently handle a significant portion of the validation infrastructure. This includes specific validations pertaining to depth, width, and ID restrictions.
Understanding what constitutes a “valid” domain model is crucial, much like any other model. In the case of recursive types, the validation of the tree structure takes precedence, as it forms the core of the type.
Thanks for reading!
Stijn
Subscribe to our RSS feed