I've described the Repository Pattern in a previous post. Those things are still valid but I want to emphasize a few aspects and to present a scenario of advanced usage.
First of all, it seems to me that way to many developers believe the Repository is there just to hide the database.Probably that's why a number of them don't understand why you need a repository when you have an ORM. Hiding the db is just one function of the Repository as it does other, more important things.
We know that the rest of the application talks to a repository when it wants something persistence related. The Application sends and receives application objects (business objects and DTOs for the view model) from the Repository, without caring what the repo uses to actual store the objects. In the majority of cases, it will probably use a (micro)ORM like EF or NH, but no matter the underlying library, the repo has to return or to handle application objects that have no relation to an ORM's entity.
This means that the Repository must 'translate' application objects to ORM entities and vice versa. The repository is pretty much like a criogenic machine: it takes something and freezes it and then unfreezes it when asked, returning a perfectly valid object exactly as it was received. So the Repository has a more active role than just simply hiding a storage device, the repository IS the governor of persistence
There isn't just a single Repository either. There are as many repositories as bounded contexts, because a repository should serve only a specific context. It might be related to persisting Domain entities or just to query and return view models , the important thing is that there are as many repositories as needed by the application.
I've said above that a repository sends back a business object when asked. But if the object is saved in 3 related tables, how does the Repository knows how to construct it? That's easy, Mike, just pass a factory to the repository's constructor and let the factory handle it. Well, I think this approach might not be the right one for 90% of the cases and that's because a repository restores an object, it doesn't create a new one. Semantics I know, but they do matter. I think it's important to make the distinction between restoring and creating a new object, especially when dealing with Aggregate Roots.
There are a number of ways to restore a complex object, I favor two:
- save and load a memento (a dto containing the object state) and then pass it via the object constructor, which can be a static factory method of the object;
- use Event Sourcing and store everything a a stream of events. When restoring the object, just pass the stream as constructor argument (this is the standard way to restore an object using ES).
When dealing with queries, things are even simpler: the repository returns a DTO and there are a number of ways to get one.This is a trivial matter and outside this post.
Advanced Usage
Now let's go a bit deeper into the rabbit hole and let's use the repository in a more advanced way. For simplicity let's consider this scenario: we have a blog (yes..) with posts and pages. Both support visibility options: they can be public or private.
public interface IVisibility
{
int Id {get;}
bool IsPublic {get;set;}
}
public class Post: IVisibility { }
public class Page: IVisibility {}
When creating a post or a page we can set their visibility but we want to to that after wards, from the admin page. Assuming we use a task based UI, this means that a request is sent to the server to change visibility for the item with id=5.
But , what do we ask the Repository for? A post, a page? How about we ask it to give us an object for which we can change the visibility? It is pretty much what we want, we don't really care if it's a post or a page, it's an item you want to make private. So how about this?
var item=repository.WantToChangeVisiblility(id);
item.IsPublic=false;
repository.Save(item);
public interface IRepository
{
IVisibility WantToChangeVisibility(int id);
void Save(IVisibility item);
}
What do you see? It's obvious that the intention is very clear, also the app receives an object which allows one thing only. If the repository would have returned a Post or a Page object we would have 2 issues:
- An explicit Post/Page will alow you to do more that change visibility. And maybe you don't have areason to do that now, but give it time. And if you don;t find a reason, I'm sure a junior developer working with that code will find one in no time.
- The repository must populate a whole business object which, depending on the desired action, can be just waste of resources. In this example, why should it load a Post with all the details and 300 comments, just to modify its visibility?!
Ok, all interesting and that, but how do we implement it in the repository? Like this:
internal class ItemWithVisibility:IVisibility{}
This class is defined in the DAL, it's an implementation detail. When the app asks for an IVisibility, the repository returns an instance of ItemWithVisibility containing only the relevant information. Easy and lightweight. And by the way, this simple object can be considered the Aggregate Root for that context. Of course, you might have a fully AR defined in the Domain which will look the same. Note that the AR is simply the object 'representing' a bounded context, it doesn't need tot be complex it that particular context doesn't require it.
This is an elegant way to use the repository in synergy with the rest of the app. It's a waste to treat the repository just as a simple dumb wrapper to hide the db, it can do more for your application.