RavenDB - Image Gallery Project (V) - The Structure

Published on 2010-10-1

Disclaimer: The structure of this application will be as simple as I can get it whilst still maintaining some semblance of maintainability going forward, any complaints can be directed at the comments field below if you think I’m committing some heinous crimes with the contents of this entry. The beauty of writing code with a high separation of concerns is that any of this can change without too much fuss if the initial code proves unworthy. This is all largely irrelevant anyway, but as the following entries will all utilise code using this structure, I thought it best to write an entry describing the basic principles of how the system works.

Views vs Entities

We have one data storage system, it stores documents and allows us to query those documents. Those documents are generally a lot flatter than those we have been used to in the past, and that allows us to store all the information required for a single entity in a single document (for the most part). A couple of assumptions therefore

When dealing with behaviour or units of work, we’re dealing with entities and by therefore the underlying documents that represent those entities.

When dealing with pages of information, or views – we’re dealing with indexes across those documents, rather than complete documents themselves

It makes sense therefore to separate these two concerns into two different systems

Views

Without getting into the detail of how we get these views out yet, views are something we need. In our application here, a view is a single class containing all the information required to render a page of information. Generally, some information will be required to know what data is required for that view – that might just be an ID, or it might be the type of view and some information about how many items are to be displayed, some search terms to look for and a few other snippets beyond that.

We therefore have two types to be aware of, the input type containing information about the view we want, and the view itself.

I’m going to be brave and define an interface here, I can always change it later on it if proves unworthy of our love

1: publicinterface IViewRepository

2: {

3: TOutput Load<TInput, TOutput>(TInput input);

4: }

And with that, an interface for our view factories to implement:

1: publicinterface IViewFactory<TInput, TOutput>

2: {

3: TOutput Load(TInput input);

4: }

If a controller action takes in a TInput as a parameter, then it can go ahead, request the view and return that for delivery. We’ll talk more about how we’ll get hold of these views later, as there are numerous ways to go about it. They could be pre-computed manually, they could be composed by aggregating multiple documents together or they could come from somewhere else entirely; they are read only.

Entities

Here is where I might make a few enemies, I won’t count the documents themselves as my entities, documents are just how I talk to the data store, have getters/setters all over them and don’t contain any behaviour – I’ll be creating entities that wrap up the documents and provide behaviour around them. This will follow the pattern of

The repository will be responsible for loading a document by id and creating the appropriate entity type around that document, and saving/deleting entities by passing their inner documents to the IDocumentSession. The entities will be responsible for enacting change to those documents; they are effectively write-only.

Since our entities themselves aren’t about state and don’t contain state (they only enact change to the document themselves), subsequent calls to the repository for the same entity won’t result in any adverse side effects as they’ll be working on the same underlying document.

We already have the necessary data to enact commands on the entity from the views, so there is generally no need to expose state through these entities, and it means that if we have to store additional data on the underlying document that doesn’t belong to this entity, our entity doesn’t suffer from this.

We could have achieved the same by sticking our behaviour on the documents, but then we have to go through some hoop jumping to make sure the entity state remains valid, to create test data (if we make the state itself private) and to serialize/de-serialize the documents.

This is a case of my experience with teams of developers who if they “can” do something the easy way, they “will”, there is nothing to stop you and nothing wrong with just having public gets/sets on your documents AND having behaviour on them – this is probably more appropriate for this kind of project anyway

Note, on a simple project like this, there would probably be no problem with just using the documents directly and enacting change on them via separate “scripts” within a transaction – I choose to do this because I want to show I’d use RavenDB to solve a more complicated problem/project.

Unit of work

I mentioned in the previous entry that I was going to leave committing changes to the application itself, I’m going to assume that in our application we’ll be able to represent all of the changes required by a HTTP post action with a single class structure, and for ease of understanding we’ll call that a Command. For now we’ll go ahead with the understanding that we can fire off a command to a magical interface and that interface will take care of the unit of work.

1: publicinterface ICommandInvoker

2: {

3: void Execute<T>(T command);

4: }

The command invoker will look for an appropriate handler (seen below) and pass the command to that for processing (most likely it will retrieve entities by ID and call methods on them)

1: publicinterface ICommandHandler<T>

2: {

3: void Handle(T command);

4: }

The Command Invoker implementation will be responsible for finding the appropriate command handler to execute and calling SaveChanges at the end of this process, flushing any changes through to the underlying store.

Implementation of the above

The above all uses StructureMap magic to locate the appropriate handlers/view factories in much the same way we’ll be using StructureMap to find validators, model binders and other such niceties.

In the next instalment of this series, we’ll create our user document, our user entity and the necessary infrastructure required to create/retrieve user entities from the repository, as well as demonstrating the functionality of the ICommandInvoker that we’ll be using throughout this series.