Agile Legacy Code

Primary Menu

Repository Pattern

If you have ever heard of software design patterns, you have heard of the Gang of Four and their book. It’s the bible on this matter and it would be a disservice not to reference it, but the pattern we will talk about is missing from this book. You instead can find more information here.

In short the repository is an object that sits between your application data and your business logic. It helps (among other things) remove duplicated code, improve testability and provide a strong typed business entity that helps catch errors at compile time.

Repository are used to isolate from data source like databases, file system or a Sharepoint list, and from other difficult to integrate objects like web services. When all your business logic deals with data retrieval or persistence through a repository, you can test your business logic in isolation replacing the real repository with a test double.

To have a better understanding of what’s going on let’s have a look at some code

classPerson

{

publicintId { get; set; }

publicstringName { get; set; }

publicDateTimeBirthDate { get; set; }

publicstringAddress { get; set; }

}

interfaceIPersonRepository

{

PersonGet(intid);

voidCreate(Personperson);

voidUpdate(Personperson);

voidDelete(intid);

}

classPersonRepository : IPersonRepository

{

privatereadonlySPListlist;

privatereadonlyPersonSPListItemMappermapper;

publicPersonRepository(SPWebweb)

{

list=web.Lists["Person"];

mapper=newPersonSPListItemMapper();

}

publicPersonGet(intid)

{

SPListItemitem=list.GetItemById(id);

Personperson=mapper.From(item);

returnperson;

}

publicvoidCreate(Personperson)

{

SPListItemitem=list.AddItem();

mapper.Populate(item, person);

item.Update();

}

publicvoidUpdate(Personperson)

{

SPListItemitem=list.GetItemById(person.Id);

mapper.Populate(item, person);

item.Update();

}

publicvoidDelete(intid)

{

SPListItemitem=list.GetItemById(id);

item.Delete();

}

}

classPersonSPListItemMapper

{

publicPersonFrom(SPListItemitem)

{

returnnewPerson

{

Name=item.Title,

Id=item.ID,

BirthDate= (DateTime)item["DateOfBirth"],

Address= (string)item["Address"]

};

}

publicvoidPopulate(SPListItemitem, Personperson)

{

item["Title"] =person.Name;

item["DateOfBirth"] =person.BirthDate;

item["Address"] =person.Address;

}

}

Here we see, in oder, the business entity we are interested on (Person), with a few basic properties, then the interface of our repository with a few basic CRUD operations, then we implement said interface against a sharepoint list, at the end we have a utility class that’s meant only to map to and from a sharepoint item.

The repository pattern is quite simple to comprehend, what’s important is that:

You keep following it

Every object in the business layer interacts with the entities through a repository

Every interaction is isolated by an interface

The following is a business layer object that expects a repository in its contructor and uses it to create a Person.

internalclassPersonManager

{

IPersonRepositoryrepository;

publicPersonManager(IPersonRepositoryrepository)

{

this.repository=repository;

}

publicvoidCreate(stringname, stringaddress, DateTimebirthDate)

{

Personperson=newPerson

{

Name=name,

BirthDate=birthDate,

Address=address

}

repository.Create(person);

}

}

As you can see this class has no knowledge of what is Sharepoint and can be tested quite easily by passing a spy to the constructor instead of the real implementation.

All operations to persist a business entity pass through a repository, and any data retrieved by a query comes from it. In my example you can see a simple get by id query, but more complex queries are allowed. You can implement them in two ways:

The repository has a method for each query

The repository receives a query object that contains the required information to build a query

Another important advantage of using the repository pattern is that it can provide a centralized caching layer to for the rest of the application. This way if a query has already been served the repository can return a cached result set without having to muck the details of the business layer. This is quite useful when dealing with repositories that mask web services.

In the end, repository help isolate data access code, from the rest of your application, it provides an insertion point to test your business layer but adds another abstraction layer so it can be difficult to understand for developers in your team whom are unfamiliar with this pattern. Also keep in mind that repositories tend to be hard to unit test, so it is better to write integration test for them.

Author: Maurizio Pozzobon

Maurizio has 5+ years developing solutions in the insurance industry. He is passionate about doing the right thing right, so he works in a tight loop with his clients to deliver the best solution possible.