I thought I'd share a solution and ideas around a problem that seems to come up from time to time in past systems I've built. I'll walk you through the implementation of a modified Snapshot Pattern in C#.
First off, what is the Snapshot pattern, and why would we use it? Put simply, a Snapshot is the concept of giving date/time context to an object, so that a current time period is factored in to any queries or commands we send to it. Here's an example of a simple problem that it can solve:
Say we have a Product object, which generically represents something that a person can purchase from an online store. A Product has both Name and Price properties. The problem is that the Price of a Product can change over time. We want to be able to ask the Product its Price for any possible date, and have it return the Price as it stood at that time. We do not want to have to pass around a DateTime parameter all over the place, however.
The following diagram shows us how we want the code to end up looking:
There are three main objects that form the backbone of our modified Snapshot pattern:
- Master: This object is persistent, and holds any data that does not change over time. In our little example with Products, the Name property would hang off a Master class. The Master holds a list of many Details, which I explain next...
- Detail: This object is also persistent, however it holds any data that may change over time. This is where our Price property lives. You'll notice that the Detail contains StartDate and ExpiryDate properties, which represent the time period during which it is valid.
- Snapshot: This is a non-persistent object that holds exactly one Master, and one Detail. The Snapshot object delegates its calls to persistent data to both the Master and Detail of which it is composed. It also holds all our business logic.
Our code for the Product scenario ends up looking like so:
The important thing to remember is that within our domain we only want to work with the Snapshot implementation, not the master or the detail. Once we've constructed a Snapshot using the Master.CreateSnapshot(date) method, we're good to go.
Hopefully this makes sense. I've used this pattern in two projects now, and very heavily on my current project. It helps to clean up the code base by removing a whole bunch of GetPrice(date) type methods, and frees the developer from having to think about time when utilizing an object. It does require some discipline, however, to ensure you don't get business logic creeping in to the Master and Detail classes.
I've also included the code, with some unit tests that you can download.
11-16-2006 6:33 PM