RavenDB - Image Gallery Project (V) - The Structure

Published on 2010-10-1

The code for this and all other entries can be found here: http://github.com/robashton/RavenGallery/ 

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

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:  public interface IViewRepository
   2:  {
   3:          TOutput Load<TInput, TOutput>(TInput input);
   4:  }

 

And with that, an interface for our view factories to implement:
   1:  public interface 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

   1:      public class WrappingEntity : IEntity<WrappedDocument>
   2:      {
   3:          private WrappedDocument innerDocument;
   4:   
   5:          public WrappingEntity(string someProperty, string someOtherProperty)
   6:          {
   7:              innerDocument = new WrappedDocument()
   8:              {
   9:                  SomeProperty = someProperty,
  10:                  SomeOtherProperty = someOtherProperty
  11:              };
  12:          }
  13:   
  14:          public WrappingEntity(WrappedDocument innerDocument)
  15:          {
  16:              innerDocument = innerDocument;
  17:          }
  18:   
  19:          private WrappedDocument IEntity<WrappedDocument>.GetInnerDocument()
  20:          {
  21:              return innerDocument;
  22:          }
  23:   
  24:          public virtual void PerformSomeAction()
  25:          {
  26:   
  27:          }
  28:   
  29:          public virtual void PerformSomeOtherAction()
  30:          {
  31:   
  32:          }
  33:      }

 

Because we shouldn’t ever need to query for entities, the interface for getting/saving/deleting entities will be very simple and look something like this:

   1:      public interface IEntityRepository<TEntity, TDocument> where TEntity : IEntity<TDocument>
   2:      {
   3:          TEntity Load(string id);
   4:          void Add(TEntity entity);
   5:          void Remove(TEntity entity);
   6:      }

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:      public interface 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:      public interface 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.