Domain Model Validation In Kotlin: Part 1

Inline classes: more type-safety for less runtime overhead

sources: pixnio & flickr
  • To get rid of the side effects (here: the exception IllegalArgumentException thrown by require); we want to know what to expect from a function just by looking at its signature, and we can only find out about this exception by looking at the implementation or by having it documented (or at runtime, ouch!).
  • If we had multiple validation errors (e.g. there are more fields to validate), only the first error would be thrown, and accumulating errors would not be straightforward.
  • the invalid variant (Invalid<ValidationErrors>) if the email is not valid; we will create Invalid instances using the invalidNel() extension function;
  • the valid variant (Valid<Email>) if the email is valid; we will use the valid() extension function to create Valid instances;
  • Smart constructor that looks like the actual constructor, using the operator fun invoke instead of a custom function name. This can be astonishing because a constructor, by specification (!), should evaluate to an instance (non-null) of the homonymous class.
  • Overloading the invoke operator to extract the value in the inline class, this way, we can refactor email.value to email():
  • Create ValidationErrors with lazy messages, using again the invoke operator:
  • validate multiple fields and accumulate the errors
  • validate each element of a list in an elegant way
  • apply validations that depend on multiple fields
  • create (extension) functions to improve readability
  • apply validations that need an external context (and here we’ll see the power of the context receivers feature, added in Kotlin 1.6.20)

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store