Cesar Aguirre
Posted on February 7, 2022
I originally published an extended version of this post on my blog.
Are you intrigued by functional programming? But you find it difficult because of all definitions and slang? Monads, monoids, endofunctors…This is a good starting point to jump into the functional world.
"Domain modeling made functional" teaches to capture requirements, constraints, and business rules in a system using types.
All the code samples are in F#. But, you can port the concepts from this book to your Object-Oriented language using built-in features or third-party libraries.
These are the two main lessons I learned from reading this book.
1. Express Domain Restrictions with Types
Express restrictions in your design and enforce constraints with new types.
In an order processing system, don't use integers to represent unit quantities in orders. A unit is a concept in the business domain. It should be in a separate class like UnitQuantity
.
To restrict unit quantities between 1 and 1000, create a private constructor in the UnitQuantity
type and only expose a factory method with the validation.
To enforce that an order should have at least one line item, create a NonEmptyList
instead of a possibly empty List
.
To represent optional values, don't use null values. Use an optional type, like Option<T>
, similar to nullable primitive types in C#.
2. Make errors part of your domain
Follow the Signature Method Honesty principle by documenting all possible outputs of a method in its signature.
For example, a Divide
function shouldn't throw an exception. Instead write, int Divide(int a, NonZeroInt b);
Stay away from exceptions. Instead, use a Result
type to wrap failed and successful values.
For example, a method to validate addresses should return Result<CheckedAddress, AddressValidationError>
. It means either a valid address or a validation error.
public Result<CheckedAddress, AddressValidationError>
CheckAddressExists(UnvalidatedAddress address)
{
//...
}
public class AddressValidationError {}
public class InvalidFormat : AddressValidationError {}
public class AddressNotFound : AddressValidationError {}
To work with exceptions in third-party code, wrap that code in a function that catches exceptions and returns a Result
. Don't catch all exceptions, only those relevant to the domain. Like, timeouts or failed logins.
Voilà! These are the two main lessons I learned from this book. If you don't work with a functional language, you can still take advantage of the concepts of this book in your everyday programming.
Hey, there! I'm Cesar, a software engineer and lifelong learner. Visit my Gumroad page to download my ebooks and check my courses.
Happy coding!
Posted on February 7, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.