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 Ranges 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 NSDates 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.

Browse the blog archive