all Technical posts

Domain Operations as F# Lenses

Lenses are a great way to interact with immutable structures. This post goes over a possible approach to using lenses with a strict domain model.

Domain model with restrictions

The domain model used in this post is a representation of a guest list for a venue. Each guest has a registered name, each name is unique on the guest list. The model is simple by design, focusing on the lenses and how they can incorporate domain operations.

👀 FPrimitive is used here to set up the basic rules of the domain during the creation of the models. A note on this, is that both the model as the error user messages are kept minimal. The most important thing to note is that we cannot just create a guest or a guest list with just any value. The result will always be a F# Result.

Lens with result type

The available F# libraries on lenses usually set up the basic get/set/map functionality, but besides maybe some optional types, there is no hook to using the Result type. It is a built-in type, and one of the reasons for this is that the error type is dependent on the situation. Luckily, the domain operations are usually within the same scope as when creating the domain model. In other words: the error type of creating a domain model or changing a domain model could be the same.

This opens up an opportunity to use lenses with our domain model. We only have to ‘hook’ it into an existing library. This post uses the Aether-way of doing lenses as an example. Don’t worry too much about the details. The most important thing here is the ResultLens type itself. This type of alias shows how any given type has to go through a Result before it can either get or set a value via the lens.

Domain operations as lenses

What this lens now provides is an alternative to interacting with the domain model. Normally, you would go through the necessary CRUD operations and provide them one by one for the model. Lenses go through this process a bit differently. Here are some examples.

If we want to update the name of one of the guests on the list, we can compose lenses together.

The composable nature of lenses makes this operation simple and, maybe most importantly, makes it available with less code to test and write. It uses the single factory function on the model to act on the model like it was publicly available for change. This is while in the background, we still have our validation result to verify the correctness of the model at each stage of the process.

Lenses can also be used to build up domain operations that you expose as a single function.

Conclusion

Lenses are a natural result of making your model immutable. They remove any duplication related to the interaction of values of your model. What previously struck me as a missing piece in using lenses for domain models, was circumvented by introducing the built-in F# Result type in the signature of the lenses. That way, each interaction with the model is validated. It provides the best of both worlds, as it still keeps your model secure throughout the way, but provides the necessary hooks to allow for flexible interaction.

Thanks for reading!
Stijn

Subscribe to our RSS feed

Hi there,
how can we help?

Got a project in mind?

Connect with us

Let's talk

Let's talk

Thanks, we'll be in touch soon!

Call us

Thanks, we've sent the link to your inbox

Invalid email address

Submit

Your download should start shortly!

Stay in Touch - Subscribe to Our Newsletter

Keep up to date with industry trends, events and the latest customer stories

Invalid email address

Submit

Great you’re on the list!