The Repository Pattern

In the context of a question asked on the RavenDb mailing list I've decided to share my thoughts on the Repository Pattern.

It is a common mistake to consider the Repository pattern a generic "good practice", without actually considering what it is good about it.

According to Martin Fowler's P of EAA a Repository

"Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects."

This makes a lot of sense, and even if you are dealing with an anemic domain model, you don't want to have data mapping code mixed in with your domain object code. The key here is "data mapping code". You don't want code that deals with the internal representation of your data storage (whatever your actual storage is) mixed with code that performs business operations on your objects.

Back in the day, when we used to write SQL code in our apps, it was essential that you implemented a layer that encapsulated the SQL queries and SQL mapping code from the rest of the application. With the technology choices available today, we rarely need to write SQL code or SQL mapping code. Today we have NHibernate, Entity Framework, Linq2SQL, RavenDb etc.

Nhibernate

Consider NHibernate - the session you open acts like an in-memory collection of domain objects, where you can query, fetch and add domain objects without needing to do any SQL specific stuff. The data-mapping and SQL stuff is encapsulated in your NHibernate mappings code. The NHibernate session IS your repository. It also acts as a Unit of Work, but that is a different pattern.

RavenDb

The same thing happens with RavenDb's client. It provides a "session" which encapsulates all the persistence details like http calls, (de)serialization, caching, concurrency, a lot of safety checks, while acting like an in-memory collection of domain objects. When you do session.Load() or session.Query(), you do this in a very similar way to what you would do when dealing with an in-memory collection of domain objects. The RavenDB's session IS your repository.

Complete Abstractions?

Do HNibernate or RavenDb sessions hide ALL the details of the persistence engine? No, but you don't want them to be hidden from you. Except for the most trivial applications, you can't ignore the way your data is persisted. You are going to need to handle relationships between the objects in your persistent store, you are going to need to handle concurrency problems and you are going to have to write efficient queries. All these concerns are application specific and no abstraction is going to be good enough to handle all your application's use cases. But the concept of session from NHibernate or Raven does provide an abstraction over the persistence details, while exposing to the developer ways in which efficient operations can be performed.

There is also a very strong resemblance between the session from RavenDb and the session from NHibernate, but that is probably because Ayende is (was?) one of the most active and knowledgeable developers and "evangelist" for NHibernate, and also because the session is a good model for a unit of work. Ayende also has some good posts about the Repository pattern.

Testing

One question that always pops up in the context of the Repository pattern is Unit Testing. How do you mock the ISession from RavenDb or NHibernate. Well ... you don't. It's very hard to mock a non-trivial repository. Instead you "use the real thing", just in an in-memory form. RavenDb's ecosystem provides an embedded, in-memory store that is perfect for running unit tests against the real store. NHibernate provides a SQLite back-end which, while not perfect, can be used for fast tests against a real sql implementation.

Conclusion

In conclusion, before you start defining the IRepository which i know sounds like a cool "good practice", stop for a minute and check if you are not already using an abstraction over your persistence.

Clean Coders .COM

Lately I've started watching episodes from CleanCoders.com, a podcast created by Uncle Bob ( Robert C. Martin ) and i must say they are excellent.

I must confess that when I've purchased the "Names" episode I was a bit skeptical about how much can be said about how to name the things you use in your code. I have already been paying much attention to names and overall "cleanness" of my code so I was not expecting to learn a lot of new things, but as usual, Mr. Martin as blown my mind with his way of explaining the "hows" and "whys" of doing things.

I can say almost the same about the "Functions" episode. Very important small details that even if I was applying previously in my code, I can now actively think about them when writing code.

Also for me one important gain from watching the videos was that now I can better explain to others the importance and details of clean code using the arguments and reasoning of Uncle Bob.

I highly recommend following all the videos on cleancoders.com. No matter if you are a junior dev or have lots of years of experience I'm sure you will gain a lot of knowledge from them. The videos are created in a non-conventional and funny way which makes them pretty entertaining to watch.

Thank you Uncle Bob for the great videos.

Continue to Clean Coders.

I'm becoming a fan boy

I could not resist anymore and i've bought a mac. I've been contemplating with the idea of getting a laptop lately and the mac book air was always coming at the top of the search results.

Finally i could not delay it anymore and one morning i wake up, went to the apple store, paid 2000$ and got the all new shiny 13 inch Mac Book Air.

My expectations were to get some good and reliable hardware, with a toy OS where i can use bootcamp to run windows 7 for everything. Boy was i in for a surprise.

The hardware is not good - its 10 levels above good, it extraordinary, everything just works great, from wake up time which is practically zero to install mono develop and create my first asp.net app with mono in less than 1 minute. I don't care what anybody says apple is light years ahead the competition when is comes to hardware quality and especially design. Everything just fits together and leaves you the feeling that somebody has invested a good amount of thought in every little aspect of the product.

Now comes the true surprise: Mac OS X 10.7.3 Lion - my first OS X system.

Mac OS X Lion

I don't even know where to begin - it took me less than a minute to make a screen shot of the above window ( command-shift-4, space, click window ) and drag it in the MarsEdit that i'm using to write this. I was writing an email in italian and i'm terrible at writing italian and i notice that iMail starts correcting my words in proper italian - without ever configuring anything related to italian. I was able to setup email accounts without any frustration, setup all IM accounts, twitter, vpn, RSS reader and everything i use daily without having to spend a lot of time google for it or trying to guess what it is that i'm supposed to do.

I've completely renounced at the idea of using bootcamp to run windows. Sure i'll install parallels or vmware to run a VM with windows mostly for Visual Studio, but for now i'm in no hurry to do that.

Final word: If you tried it already you have most probably switched to OS X, if you have not tried it and you have the $$ go for it and you will enjoy a new way of using a computer.

I'll just enjoy the honeymoon with my new powerful "toy".

*UPDATE: *and then i found parallels with coherence ... and i still don't believe this it true ... i run visual studio like any other mac app. Somehow i've been in this field for more than 15 years and i just discover this now... and i don't think there is any going back. There is absolutely no reason for to use anything else. And this comes from someone who has used FreeBSD as a main OS for years. Again - if you have the money, it will blow your mind, don't hesitate another minute.

Hopefully i'll keep the posts coming.

Test Data Generator

I've pushed to github a new project, TestDataGenerator that should help with filling random objects with data. I felt the need for a tool like this when testing various serialization techniques and persistence strategies.

Basically this utility should construct the instance of an object using a public constructor, and fill all it's public, writable properties with random data.

Sample usage:

 1 class Sample
 2 {
 3  private readonly int intValue;
 4  private readonly string stringValue;
 5 
 6  private Sample()
 7  {
 8      this.intValue = -1;
 9      this.stringValue = null;
10      this.StringProp = null;
11      this.DateProp = DateTime.MinValue;
12  }
13 
14  public Sample(int intVal, string stringVal)
15      :this()
16  {
17      this.intValue = intVal;
18      this.stringValue = stringVal;
19  }
20 
21  public int PrivateInt { get { return intValue; } }
22  public string PrivateString { get { return stringValue; } }
23 
24  public string StringProp { get; set; }
25  public DateTime DateProp { get; set; }
26 }
27 
28 [Test]
29 public void Catalog_Can_Create_Using_Consutrctor()
30 {
31  Catalog catalog = new Catalog();
32  object instance = catalog.CreateInstance(typeof(Sample));
33 
34  Assert.IsInstanceOfType(typeof(Sample), instance);
35  Sample sample = instance as Sample;
36 
37  Assert.AreNotEqual(-1, sample.PrivateInt);
38  Assert.IsNotNull(sample.PrivateString);
39  Assert.IsNotNull(sample.StringProp);
40  Assert.AreNotEqual(DateTime.MinValue, sample.DateProp);
41 }

For more samples see unit tests.
I'll try to add more cases ( abstract class, interface implementation discovery ) in the future. You are welcome to submit sample classes, preferably as unit tests.

RavenDb EnsureDatabaseExists extension method

Just a quick hint for others like me, who spent some minutes searching for the EnsureDatabaseExists method when trying to use RavenDb with Multi-Databases.

EnsureDatabaseExists is an extension method on IDatabaseCommands defined in the Raven.Client.Extensions namespace.

To make it work you need to add a using statement for this namespace.

 1 using Raven.Client;
 2 using Raven.Client.Extensions;
 3 
 4 using (DocumentStore store = new DocumentStore()
 5 {
 6     Url = "http://localhost:8080/" 
 7 })
 8 {
 9     store.Initialize();
10     store.DatabaseCommands.EnsureDatabaseExists("SomeDatabase");
11 }