Building a Rich Domain In Iterations
I’m currently revising code from 3 years ago. The result is a replica of the old stuff – updating isn’t worth the effort, it’s written in Swift 3 and the code base is small, so I rewrite it and copy useful parts over. The public interface already differs from what I wrote 3 years ago.
In the first step, I translated the old code but took what was there. For example, back then I had a DateRange
type. Swift Range
s were less powerful back then, so I added a couple of convenient methods that I can now replace with the standard library protocols instead. So I demoted the type to typealias DateRange = Range<Date>
. I also had a Date
type (this was before NSDate
dropped the “NS”) that I renamed to NormalizedDate
. In the domain I work on, a date isn’t a date-time-combination, but just the year, month, and day. Confusingly, this type was a mere wrapper for NSDate
that dropped the time information for quick comparisons.
Later, as I worked on the protocols that this module would expose, I figured that the notion of a “range of days” is missing. Sure, Range<NormalizedDate>
did the job, but a DayRange
type was better. It’s became a RawRepresentable
, backed by Range<NormalizedDate>
.
After this change, though, I found the name “normalized date” too general. Sure, I am normalizing the date-time to drop the time information completely. But I really want to work with a Day
-date type. So I introduced Day
, which is backed by a NormalizedDate
and specifies its own normalization rules. This is oddly satisfying. The Day
type knows the normalization rules to create a NormalizedDate
from a regular old NSDate
.
As I write about all this, I notice that I should’ve made an additional step and replaced Day
with a struct that has year
, month
, and day
only and constructs NSDate
s where needed, instead of relying on that type to represent its values. Excuse me for a bit while I change the domain to accommodate this insights.