Apr 22

Today we’re going to keep looking at the SharpArch.Core assembly and try to understand its default support for repositories. Before going on, I guess that it’s a good idea to take a detour and talk a little bit about repositories. One of the problems we (developers) face in most applications is that most of the time we’re so worried with infrastructure code that we loose the so needed domain perspective. As we’ve seen in previous posts, we’ll have two basic type of objects: entities and value objects.

I believe that we all agree in that we have two ways of getting a reference to an object (entity or value object): we can create a new instance by invoking its constructor or we can get a reference to an existing one by traversing an association. In this second case, you start by getting a reference to a know object and then you ask it for a reference to the object you’re interested in. Now, this is all good and simple when we think about OO code, but, again, when we start mixing these concepts with database persistency, things become a lot more interesting (or should I say, difficult?).

I guess that we could always let the database leak into our domain and let users execute a queries over it. Unfortunately, this is nor really a good idea since it tends to lead to a shift on focus from domain to data based design (where the database is king). There are also other  problems…for instance, if you have a model where a customer has lots of addresses and these objects are persisted on a database,then what should you do when:

  • a customer is loaded…should you get the customer and his addresses? only the customer?
  • Should we allow isolated loading of address?

As always,the answer to the previous questions depends on the *context*. DDD answers them by introducing aggregates and repositories for giving you access to objects (aggregates) that been persisted in a database (and are in the “middle” of their life cycle).

Today I’m not going to spend a lot  of time talking about aggregates. I’ll just define them as being a cluster of entities and value objects which have a single access point (also known as aggregate root). You should only hold references to this top level entity, though you can pass “transient” references to the inner entities or value objects. By using aggregates, you can easily maintain the consistency between a group of objects (which is one of the problems we face when working with complex domains).

If you understand the concepts presented in the previous paragraph, then it should be obvious that repositories should only work over aggregates! A repository abstracts the database and gives the developer the illusion of working with a collection of aggregates. As you might expect, you can add and remove aggregates from this “pseudo in-memory” collection. They should also give you references to existing objects (that is, objects that are stored in the database). In this case, you’ll probably have several methods which allow you to specify different filters (or you can even use something like Fowler’s Query Object pattern).

After all this, you shouldn’t be surprised to hear that S#arp framework introduces an interface for representing repositories. It’s called IRepositoryWithTypedId<T> and it looks like this:

public interface IRepositoryWithTypedId<T, IdT> {
        T Get(IdT id); 
        IList<T> GetAll(); 
        IList<T> FindAll(IDictionary<string, object> propertyValuePairs); 
        T FindOne(IDictionary<string, object> propertyValuePairs); 
        T SaveOrUpdate(T entity); 
        void Delete(T entity); 
        IDbContext DbContext { get; }

As you can see, in this case we need two generic type arguments: one for representing the type of element the repository works with (T) and the other to set the type of the ID field (IdT). Besides the expected methods (Get, GetAll, SaveOrUpdate and Delete, which are self explanatory), there’s also a couple of members which deserve special attention:

  • FindAll receives a dictionary which contains several entries that filter the returned result set. You’re supposed to use the name of a property as the key and its value for specifying the desired values of that property. The method will return all the elements that have those values in the specified properties;
  • FindOne: similar to the previous method, but in this case, it will return a single instance;
  • DbContext gives you a reference to an object that lets you have access to the general database context associated with the current call.

Since you might be a little confused with DbContext (of type IDbContext), here’s the current API of that type:

public interface IDbContext {
        void CommitChanges();
        void BeginTransaction();
        void CommitTransaction();
        void RollbackTransaction();

As you can see, this interface encapsulates all the things you generally need to do in order to work with transactions.

There’s also a specific IRepository interface which uses an int for the ID’s type:

public interface IRepository<T> : IRepositoryWithTypedId<T, int> { }

As we’ve seen in a previous post, S#arp is heavily influenced by NHibernate. So, it shouldn’t surprise you to see that there’s a specific interface that gives you access to all the things you’re used to when working with NHibernate:

public interface INHibernateRepositoryWithTypedId<T, IdT> : IRepositoryWithTypedId<T, IdT>    {
        T Get(IdT id, Enums.LockMode lockMode); 
        T Load(IdT id); 
        T Load(IdT id, Enums.LockMode lockMode); 
        IList<T> FindAll(T exampleInstance, params string[] propertiesToExclude); 
        T FindOne(T exampleInstance, params string[] propertiesToExclude); 
        T Save(T entity); 
        T Update(T entity); 
        void Evict(T entity);

public interface INHibernateRepository<T> : INHibernateRepositoryWithTypedId<T, int>, IRepository<T> { }

As you can see, the previous repository interface is augmented to give you full control over the things you’re used to setting when working directly over ISession instances.INHibernateRepository is there for the most used scenario: when the Id property is stored as an integer in the database (just like we had with Entity vs EntityWithTypedId for entities).

And that’s it. Keep tuned for more in S#arp

1 comment so far

  1. cash advance
    8:52 pm - 12-12-2009

    Good Day!!! is one of the most excellent innovative websites of its kind. I enjoy reading it every day. Keep it that way.