Functional Domain Modeling
Lately, I've been reading about functional paradigm and related programming languages. More I read about it, more I feel that modeling domains in pure C# (or Java for that matter) is unnatural and does not communicate the intent of the domain clearly.
After so many years of writing C# code, that's quite a statement to make. But, I am serious. Lets model a simple subdomain Order for a hypothetical e-commerce company using F# then later using C# and see which of the models looks more expressive.
Lets start with C# first. Below is a typical Order
entity with some typical properties.
(For brevity, I have removed other properties & methods which add behavior to this model.)
At first glance, this model looks decent. Technically, it has all the properties required to represent an Order
. It uses ubiquitous language for modeling the domain, it uses strong types to represent attributes of an order (so, the smell of primitive obsession is also taken care of). The properties expose only public getters, so state change (if we choose to do so) is only possible via public methods. But, does this model document the design of Order subdomain? Can a developer look at this model and understand what are the business constraints in this sub-domain? The answer is No. Let's examine this model closely to see why.
Lack of Choice
The PaymentMethod
property is of type IPaymentMethod
. Typically, this interface would be implemented by concrete payment method classes like CashOnDelivery
, CreditCard
Paypal
etc. But, this property does not scream about all the supported payment methods of the order sub-domain.
Of course, I can always see what are the types implementing IPaymentMethod
interface, but, its well.. ehm... unnatural ...(you will see why when we examine the F# model). In short, the payments methods are not explicitly documented in the model. Another problem with having an interface is that it does not allow the domain modeler to make the property hold different types (or combination of types) of values depending on a particular payment method. For instance, in case of CreditCard, I would like the property to hold Card number and CardType information, if the payment method is PayPal then the property should hold the paypal Id etc.
Another problem, though minor, is that we do not know if we are dealing with a closed set of payment methods. What if an innocent developer makes an innocent mistake and adds MyAwesomePaymentMethod
as another implementation of IPaymentMethod
. So, now Order
is secretly supporting a new payment method and no one (include the innocent developer) has a clue about it.
Here comes F# for the rescue. F# has discriminated unions. In other words a choice type, which, if used, unambiguously documents about the only three supported payment methods available. In other words, its a closed set of payment methods. Below is how the PaymentMethod
type would be modeled in F#. Looking at this file, one can see the complete spec of PaymentMethod
type.
If I want to print a payment method (Of course, I wouldn’t from a domain model, perhaps I would execute a function for each matched payment method), I can pattern match and print (or do something more useful) in here.
Notice how pattern matching allows us to return different information depending on the choice of payment method. In case of Paypal, I can use the PaypalId. Similarly, in case of Card
payment, I can use the card number and card type.
Coming back to C#, I can use pattern matching and try to mimic the F# choice type behavior. But its ugly and convoluted (IDK why I am even mentioning it here :D ).
Stuck without Option
Let's take a good look at the C# domain model once again. Can you infer which properties are required and which are optional? Typically, an order may or may not have a discount code which makes the property DiscountCode
optional. But, the C# model fails to document that constraint. You may argue that DiscountCode
could be made nullable
and then all is good. But, is null
equivalent to not having a discount code?
In F#, we can use Option<'a>
to denote an optional field as shown here:
This is a self documenting domain model. By looking at it I know that an order may or may not have DiscountCode
.
Luckily, Language Ext package allows us to do this in C#. The Order
model in C# (using Language Ext) now looks like this:
Much better!
As mentioned, an Option
may or may not have a value. This is denoted with Some
or None
respectively. Below is how one can inspect / match an Option
to see if it contains a Some
or a None
.
That's a short one on why I feel that functional languages are naturally good for modeling domains as they make domain models expressive and self documenting. I am still experimenting with a couple of functional languages and so far I can see clear benefits of functional languages over imperative languages when it comes to developing a project with domain driven design.
I am not sure how easy or painful it is to access database or perform I/O with a functional language. I would probably run into it at some point. Do share your experiences (good and /or painful) with functional languages under comments and Thanks for reading!