The Integrity of Simple Values

Domain Modeling Made Functional — by Scott Wlaschin (57 / 125)

The Pragmatic Programmers
The Pragmatic Programmers
4 min readJan 28, 2021


👈 Chapter 6 Integrity and Consistency in the Domain | TOC | Units of Measure 👉

In the earlier discussion on modeling simple values, we saw that they should not be represented by string or int but by domain-focused types such as WidgetCode or UnitQuantity.

But we shouldn’t stop there, because it’s very rare to have an unbounded integer or string in a real-world domain. Almost always, these values are constrained in some way:

  • An OrderQuantity might be represented by a signed integer, but it’s very unlikely that the business wants it to be negative, or four billion.
  • A CustomerName may be represented by a string, but that doesn’t mean that it should contain tab characters or line feeds.

In our domain, we’ve seen some of these constrained types already. WidgetCode strings had to start with a specific letter, and UnitQuantity had to be between 1 and 1000. Here’s how we’ve defined them so far, with a comment for the constraint.

​ ​type​ WidgetCode = WidgetCode ​of​ ​string​   ​// starting with "W" then 4 digits​
​ ​type​ UnitQuantity = UnitQuantity ​of​ ​int​ ​// between 1 and 1000​
​ ​type​ KilogramQuantity = KilogramQuantity ​of​ decimal ​// between 0.05 and 100.00​

Rather than having the user of these types read the comments, we want to ensure that values of these types cannot be created unless they satisfy the constraints. Thereafter, because the data is immutable, the inner value never needs to be checked again. You can confidently use a WidgetCode or a UnitQuantity everywhere without ever needing to do any kind of defensive coding.

Sounds great. So how do we ensure that the constraints are enforced?

Answer: The same way we would in any programming language — make the constructor private and have a separate function that creates valid values and rejects invalid values, returning an error instead. In FP communities, this is sometimes called the smart constructor approach. Here’s an example of this approach applied to UnitQuantity:

​ ​type​ UnitQuantity = ​private​ UnitQuantity ​of​ ​int​
​ ​// ^ private constructor​

So now a UnitQuantity value can’t be created from outside the containing module due to the private constructor. However, if we…



The Pragmatic Programmers
The Pragmatic Programmers

We create timely, practical books and learning resources on classic and cutting-edge topics to help you practice your craft and accelerate your career.