3 ms·
Interesting article, especially encoding as much domain knowledge as possible into types by encapsulating simple strings/ints/etc into case classes. Fully agree
by marcelk 12y ago
Interesting article, especially encoding as much domain knowledge as possible into types by encapsulating simple strings/ints/etc into case classes. Fully agree :)
However, performing validation outside of the model makes it possible to create a model with invalid data. For example, calling val x = EmailAddress("@") would pass. Thus, a developer must remember to call the validation every time they create such a case class. In my experience, this wouldn't hold since developers are as lazy as the code allows them to be.
Instead, why not include requirements directly into a case class constructor. For example:
case class EmailAddress(addr: String) {
private val emailRegexp = ...
require(emailRegex.findFirstIn(addr).isDefined, s"Email $addr not valid")
}
In this way, one can be sure that an EmailAddress case class always holds a correct email and can always safely rely on this assumption. Also, one can easily test (e.g. with ScalaCheck) that the constructor throws an IllegalArgumentExceptin.
The only downside is the conversion of these exceptions into Either, Scalaz Disjunctions or Scalaz Validation. Of course, it's not that pretty and idiomatic to convert try-catch blocks into Either. But IMHO, it's a small cost in exchange for the guarantee that if your model exists it must be valid.