Repositories are a design pattern which I have never been a huge fan of. I can see the use of them as a good layer boundary, but too often I see them being used all over the place instead of at an infrastructure level in a code base.
A particularly prevalent version of this misuse I see is self populating collections. These generally inherit
Dictionary<TID, TEntity>, and provide a set of methods such as
.LoadByParentID(TID id). The problem with this is that the collection still exposes methods such as
.Remove() - but these operations only run on the in-memory entities, and don't effect the data source itself.
The technique I prefer for reads are Query objects. These are simple classes which expose a single public method to return some data. For example:
The code using this class might look something like this:
This class is almost too simple, but resembles a system's processor which I wrote. They key here is that the
DocumentProcessor only relies on an
IDocumentsQuery, not a specific query.
Normal usage of the system looks like this:
When the user requests a single document get reprocessed, we just substitute in a different Query:
And finally, when the system is under test, we can pass in completely fake commands:
This means that in the standard usage, it gets passed an instance of
GetDocumentsWaitingQuery, but when under test gets a
Substitute.For<IDocumentsQuery>(), and for debugging a problem with a specific document, it gets given
new GetSingleDocumentQuery(id: 234234) for example.
What about saving? Well it's pretty much the same story:
Obviously the sql in the save command would be a bit more complete...
Well yes, you can create methods on your repositories to do all of this, like so:
But now your classes utilising this repository are tied to the methods it implements - you cannot just swap out the workings of
.GetDocumentsWaiting for a single document query any more.
This is why I like to use Command and Query objects - the not only provide good encapsulation (all your sql is contained within), but they also provide a large level of flexibility in your system, and make it very easy to test to boot too!