The Repository Service Pattern – Developing an Online Game

It was really important to me that we started the new version of Next Gen Cricket with a really strong basic structure. For me this was the repository service pattern. In this post I will attempt to explain this pattern, how to implement it, the ideas behind the pattern, and the benefits it brings.

The Repository Service Pattern

The repository service pattern can be quite confusing when you initially look at it. I will discuss my entire structure from the data through to the service that makes the data available. This way you can see how the data is made available and how the repository service pattern works.

There are several layers in the application. We start with a core data layer. This is our data storage system, in Next Gen Cricket’s case, the database. We access and expose this data through our Repository layer. This consists of Repository classes that provide basic functions to Create, Read, Update, and Delete our data. Our final layer is our Domain. This provides the entities we want to make available (which may have a different structure to the stored data). As well as these entities, we also provide services to make this data available. In the services we provide the business logic of our application, as opposed to the few simple methods we made available in the Repository layer.

The three layers of data provides us with good separation between our data and our services. We make available entities in the form we feel most appropriate for our application, rather than the structure we store them, and we provide services which perform the complicated business logic without tightly coupling this to our data source. By providing this service layer, this is an extra level of abstraction for our data, and means we can keep our Repository simple. It would now be easy to replace the Repository with access to a different data source, as the Repository is a very simple layer, with few methods, and our more complicated service layer is unaffected by the change. As long as our Repository implements the interface for a Repository, provided by our Domain layer, our Domain layer is happy, and doesn’t need to know any more about our Repository layer. This is what keeps the layers loosely coupled.

The Core Data Layer

The Core Data layer is our data for our application. In Next Gen Cricket this is our database. We have created this as a project in Visual Studio 2012 as this means we can fully source control our database structure. There isn’t really anything different to this layer at all. We create our database like usual, separating our data into separate entities, and create new tables wherever logical.

The Repository Layer

The next layer we look at is the Repository. We use the Entity Framework to map our database to objects. This is a simple way of not having to worry about SQL queries, and fetching and saving our data in a logical way for our application.

We then expose our objects through basic Create, Read, Update, and Delete methods in our Repository. We first define a simple RepositoryBase class that our Repositories inherit:

There are a few things to explain here. We inherit the IRepository interface which defines the basic Methods we require. We also take two generics, one for the the type of entity the repository handles, and the other for the type of the Id (usually an int). The IRepository class does not actually sit in this layer, it is defined in the Domain layer. The Domain specifies what it requires from a Repository, and treats everything as an IRepository so we are totally independent from our Repository layer. It is up to our Repository layer to provide Repository classes in the way our Domain layer requires them to be. This is what allows us to easily replace our Repository layer if our data changes.

We define all our 7 methods that we use in NGC Repositories. Five of these are abstract as they are left to be defined by the inheriting class. However we define two functions here as they are the same on all our Repositories. The GetWhere() method takes an Expression, so we can fetch all entities meeting a specific criteria. This is very useful to our Service layer, as we can use this for any number of methods that would fetch by a particular criteria. For example a call to GetWhere(p => p.Team == team) on our player repository would return all our players from the specified team. Therefore in our service we can provide a GetPlayersByTeam(Team team) method that can be implemented simply. Similarly there is a GetFirstOrDefault method that works the same, but will return one Entity. This would be used in possibly the following scenario:

Next we have an example of our PlayerRepository to show how we implement our Repositories on top of the RepositoryBase:

Above is the code for the PlayerRepository class. This exposes our Player Data. As you can see our final five methods are defined here. Firstly we use dependency injection to loosely couple our Repository from our data source. This is why our constructor requires the dataContext to be passed in. I’ll explain the remaining methods below:

  • GetAll() – This simply returns all our Players. The code is simple, but it uses an extension method called Project() that Automapper makes available (Automapper is an excellent Nuget Package that allows us to set up mappings between objects, that we use to convert our Entity Framework Entities, into our Domain entities). The  Project().To<Domain.Entities.Player>() literally tells it to convert all our players to our Domain entity of Player, and it returns an IQueryable.
  • GetById(int id) – The code is exactly the same as GetAll(), except we add a  FirstOrDefault(p => p.Id == id) call to return the one (or zero) entity that matches the id we pass in. This returns a single Player Entity
  • Create(Domain.Entities.Player entity) – This is where the code gets interesting. Our remaining three methods all take an existing entity as an argument. Here we simply map our entity to our Entity Framework Entity (again using Automapper), add it to our list of players, and save the changes in our dataContext. We also return the id of the newly created Player as this is useful to know (we may want to redirect the web application to the newly created player, and without knowing the id, this would be hard). The only confusing bit here is we set the player.Nation parameter to null. This is because the player.Nation refers to a Nation entity, and the NationId is the navigation parameter. By setting the NationId property, the Nation property takes care of itself. If we didn’t do this, the Entity Framework would get confused and throw exceptions as it would see clashes between the Nation Property and the NationId Navigation Parameter.
  • Update(Domain.Entities.Player entity) – Here we first map our entity to an entity in our Entity Framework Model. Next we need to see whether an entry already exists in our dataContext’s ChangeTracker. If it does, we update this entry to the entity we have mapped. If it doesn’t (dbPlayer == null), then we first create a temporary player entity with our player id, attach it to the Players (This doesn’t add the player, it literally just attaches an existing object to the dataContext), and then update the values of this newly attached object. Now when we call SaveChanges() on our dataContext, our Player in
  • Delete(Domain.Entities.Player entity) – Finally we finish with our Delete method. Very simple, we find the entity in our dataContext that matches the entity we pass in (by the unique id), Remove this from the dataContext, then SaveChanges in our dataContext.

The Domain Layer

The Domain layer is the main business logic of our application. This is where we define how our application should work. We provide Entities (Our objects of the application, in Next Gen Cricket examples include Player, Team, Ground etc.), Services which provide methods to fetch our data in ways that make sense to our application (UserService.GetByUsername(string username), PlayerService.GetByTeam(Team team) are a couple examples of possible methods on our services). We also define the interfaces our Service requires (such as the IRepository interface mentioned earlier that the Repository implements).

Entities

Firstly I will look at our Entities. These are simply our objects that will be used in our application. We define the properties, and rules that our properties abide by. To do this, there are a couple of abstract classes to define our Entities:

The IValidatable just defines that there must be a GetBrokenRules() method. We then extend this in the ValidatableBase class by providing a list for broken rules, a way to and broken rules, providing an abstract implementation of a Validate() method (which we define in classes to find which rules are broken), and finally we define the GetBrokenRules() method, which clears broken rules, and calls Validate to recheck all rules. Simply this means that we can check each of our Entities are valid within our application by just calling GetBrokenRules() on them.

Next we extend this with a simple class to provide an Id for all our Entities. This is our EntityBase class:

Very simple, we accept a generic for the type of the Id, then define an Id Property. All our Entities must have an Id.

Finally an example of an Entity within the application. A fairly simple example is the Team class:

We first define our properties. You see some of our properties are actually other entities (User, PlayerContracts, SponsorContracts). The interesting bit here is the Validate method. This is where we define the rules our object must abide by. Here the rules are simple, we check the two strings for Colors are actually valid hex codes, and then check the team has a minimum of 11 players contracted. If any of these rules are broken, we add the relevant BusinessRule. For completeness I will also show the BusinessRule class:

Nothing special here, it just lets us define a string for a name, and a rule. In Next Gen Cricket we follow a naming structure for our rule so we can easily define resources as friendly error messages. All our Rules start with the property that is invalid, followed by an underscore, and then the reason the property is invalid.

Services

This is the real logic of our application. The services access data from our Repository, and then expose it in a way that is logical for our application. We want to provide methods that make sense to our application, rather than the generic methods our repository provides (which keeps access to our data very simple). I’ll look at the TeamService here so you can see how we make Teams available:

This is by no means the final TeamService. This is the part of the application that grows as we add more features in, as it will make sense to add more logical methods (for a start, methods to allow creation of a team). Two of our methods (GetAll() and GetById(int id)) are exact mappings of the Repository methods. However the third, GetByName(string name), is far more interesting. Here we make use of our Repositories GetWhere(Expression<Func<T, bool>> expression) method, that allows us to pass in an anonymous function to fetch our data. We simply check here whether our team name equals the name we are looking for.

Summary

I hope this has given a good overview of the Repository Service Pattern. I in no way claim to be an expert, and am learning about this structure all the time. I wanted to share this pattern as it provides a robust base for an application and has wide ranging uses. As you may have noticed I have talked nothing about user interfaces here, as this pattern allows us to easily separate our user interface from our business logic. A huge advantage of this is we can now provide an user interface for many platforms, and that is certainly my intention for Next Gen Cricket. Each user interface will access the service layer to retrieve and update the data where necessary. It is a very powerful pattern for development of medium to large scale applications.

Posts In Series:

  1. Developing an Online Game – The Previous Attempt
  2. The Repository Service Pattern – Developing an Online Game

One thought on “The Repository Service Pattern – Developing an Online Game

  1. I just wanted to let you know how much this post helped me to understand several topics, not only repository pattern, but also dependency injection (which I was struggeling with).
    Thanks a lot!

Leave a Reply