Designing with types: Conclusion
A before and after comparison
In this series, we've looked at some of the ways we can use types as part of the design process, including:
Breaking large structures down into small "atomic" components.
Using single case unions to add semantic meaning and validation to key domain types such
EmailAddressandZipCode.Ensuring that the type system can only represent valid data ("making illegal states unrepresentable").
Using types as an analysis tool to uncover hidden requirements
Replacing flags and enums with simple state machines
Replacing primitive strings with types that guarantee various constraints
For this final post, let's see them all applied together.
The "before" code ##
Here's the original example we started off with in the first post in the series:
type Contact =
{
FirstName: string;
MiddleInitial: string;
LastName: string;
EmailAddress: string;
//true if ownership of email address is confirmed
IsEmailVerified: bool;
Address1: string;
Address2: string;
City: string;
State: string;
Zip: string;
//true if validated against address service
IsAddressValid: bool;
}And how does that compare to the final result after applying all the techniques above?
The "after" code ##
First, let's start with the types that are not application specific. These types could probably be reused in many applications.
And now the application specific types.
Conclusion ##
Phew! The new code is much, much longer than the original code. Granted, it has a lot of supporting functions that were not needed in the original version, but even so it seems like a lot of extra work. So was it worth it?
I think the answer is yes. Here are some of the reasons why:
The new code is more explicit
If we look at the original example, there was no atomicity between fields, no validation rules, no length constraints, nothing to stop you updating flags in the wrong order, and so on.
The data structure was "dumb" and all the business rules were implicit in the application code. Chances are that the application would have lots of subtle bugs that might not even show up in unit tests. (Are you sure the application reset the IsEmailVerified flag to false in every place the email address was updated?)
On the other hand, the new code is extremely explicit about every little detail. If I stripped away everything but the types themselves, you would have a very good idea of what the business rules and domain constraints were.
The new code won't let you postpone error handling
Writing code that works with the new types means that you are forced to handle every possible thing that could go wrong, from dealing with a name that is too long, to failing to supply a contact method. And you have to do this up front at construction time. You can't postpone it till later.
Writing such error handling code can be annoying and tedious, but on the other hand, it pretty much writes itself. There is really only one way to write code that actually compiles with these types.
The new code is more likely to be correct
The huge benefit of the new code is that it is probably bug free. Without even writing any unit tests, I can be quite confident that a first name will never be truncated when written to a varchar(50) in a database, and that I can never accidentally send out a verification email twice.
And in terms of the code itself, many of the things that you as a developer have to remember to deal with (or forget to deal with) are completely absent. No null checks, no casting, no worrying about what the default should be in a switch statement. And if you like to use cyclomatic complexity as a code quality metric, you might note that there are only three if statements in the entire 350 odd lines.
A word of warning...
Finally, beware! Getting comfortable with this style of type-based design will have an insidious effect on you. You will start to develop paranoia whenever you see code that isn't typed strictly enough. (How long should an email address be, exactly?) and you will be unable to write the simplest python script without getting anxious. When this happens, you will have been fully inducted into the cult. Welcome!
If you liked this series, here is a slide deck that covers many of the same topics. There is a video as well (here)
Domain Driven Design with the F# type System -- F#unctional Londoners 2014 from my slideshare page
Last updated
Was this helpful?