The definition of Repository pattern is extremely clear (as usual coming from where it come). What was not absolutely clear is how developers are using it… perhaps my interpretation of these words is a little bit extreme.
A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection.In .NET a Repository should look like:
public interface IRepository<T> : ICollection<T> { }You may have just one implementation or you may have more than one especially to prevent the calls to Clear.
For example:
public class ProductRepository : IRepository<Product> { private readonly ISessionFactory sessionFactory; public ProductRepository(ISessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } public void Add(Product item) { sessionFactory.GetCurrentSession().Save(item); } public void Clear() { throw new NotSupportedException(); } ... }The other part of the pattern definition say:
Client objects construct query specifications declaratively and submit them to Repository for satisfaction.With .NET3.5 the Repository should look like:
public interface IRepository<T> : ICollection<T>, IQueryable<T> { }And the generic implementation for NHibernate should look like:
public class Repository<T>: IRepository<T> { private readonly ISessionFactory sessionFactory; public Repository(ISessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } #region Implementation of IQueryable public Expression Expression { get { return sessionFactory.GetCurrentSession().Linq<T>().Expression; } } public Type ElementType { get { return sessionFactory.GetCurrentSession().Linq<T>().ElementType; } } public IQueryProvider Provider { get { return sessionFactory.GetCurrentSession().Linq<T>().Provider; } } #endregion ... }That’s all ? … yes that’s all… or that’s should be all.
LINQ is a powerful OO query language but was not designed, in specific, for persistence; in many cases we will must divide the execution in two parts: the part to run in the server and the part to run in RAM. Actually NHibernate has the same concept, through IQuery/ICriteria (server-side) and IResultTransformer (client-side), the main difference is that you are in charge of the decision about “where-run-what”. Implement a LINQ-provider, which is able taking such decisions, is the real feat (I have shown an simple example in this post). So far I didn’t see a persistent-layer that is in condition, always, to take some of these decisions and, frankly, I think it will be very hard; probably, in some cases, we will back to “user decision” with an explicit separation of a linq-sentence in two parts.
Using a Repository interpretation like the above there is another side effect: the responsibility of “how query the persistence” is completely delegated to the business-logic developer. There is no problem because, in general, he is the same guy ? perhaps… I’m not the same guy when I’m implementing the BL than when I’m implementing a DAO (I have tried to explain the problem to my psychologist but he doesn’t understand the problem). In practice Fabio-BL is worried only by OO matters where Fabio-DAL is worried by some OO-matters and, over all, by RDBMS issues. If Fabio-BL has a request like “give me a way to have all Products”, Fabio-DAL will give him a IPaginable<Product> with some restrictions about page-size, or, after some negotiations, an IEnumerable<Product> limited to the first 500 products (no more negotiable).
And before LINQ come in play ?
Here is where I saw everything else than Repository. For example:public interface IProductRepository { Product GetById(int id); IEnumerable<Product> FindByDecription(string template); IEnumerable<Product> FindByPrice(IRange<decimal> priceRange); void Add(Product product); void Remove(Product product); } public interface ICustomerRepository { Customer GetCustomerById(string id); IEnumerable<Customer> FindByName(string name); void AddCustomer(Customer customer); }We can call the two implementations Repository as we can call it Juan, John or whatever; obviously “Repository” sound cool.
The correct way to implements Repository, before LINQ come in play, is using another pattern (the same defined for DAO) : Query object pattern.
The methods in your generic repository may look like:
IEnumerable<T> Retrieve(IQuery<T> query); long Count(IQuery<T> query);The challenge, here, is write a good generic IQuery<T> interface.
A little step back : “Repository mediates between the domain and data mapping layers”
Which “data mapping layers” ?
As the DAO even the Repository shouldn’t expose the implementation of the persistent-layer outside the Repository implementation itself and for that reason you shouldn’t use directly “a cooked” query object as NHibernate.ICriteria.
From here on any consideration we can make over “how query the domain” using Repository is applicable to DAO.
Next post DAO.
I think you're right, but I just want to add that the use of QueryObject pattern is not required when using DAO. So the interfaces IProductRepository and ICustomerRepository are perfect examples of dao interfaces.
ReplyDeleteYou can encapsulate there an HQL in the FindByPrice for instance.
So, if I'm correct I will use the Repository pattern (or Dao with QueryPatter) when I need a lot of simple-queries that could be addressed with an OO query language such as linq.
If I need a lot of smarty-persistence queries I will prefer to use a dao patter "a secas".
But, I could be wrong.
The DAO will be matter of another post.
ReplyDeleteHi Tano,
ReplyDeleteI left behind the naming battles because they are useless for the contenders and, mainly, for the audience :), anyway, the arguments alone are OK.
I call my object retrieval abstractions "Repositories" because I think that they are, conceptually, inside the DDD definition (similar to Fowler definition). I found the query object pattern difficult to implement so I felt fine with a method for each query, besides, as you described, IQueryable is immature by now. I think that this implementation difference does not invalidate the pattern.
I should say, in addition, that another important difference between DAOs and Repositories is that Repositories have two parts, the interface or protocol, which lives (and use the language) in the domain and the implementation which is infrastructure. While I have seen the DAOs use interfaces, that is only for implementation replacement (not necessary expressed in domain languaje). Thanks for the post, BTW.
there isn't a battle I said "or" and not "vs".
ReplyDeleteTano,
ReplyDeleteYour suggestion was to call "my repositories" it Juan (or IJuan< T >) just because it does not use the query object pattern. The naming battle is just around the corner :).
See you
Please Carlos, don't take it personally.
ReplyDeleteYou know that each pattern has its interpretation.
I can't write "IMO" in each assertion in my blog and perhaps is unneeded; when I'm writing I don't have other option than write my opinions.
Personally? naaahhh. I'm just stealing your blog to post another opinion.
ReplyDeleteThanks!
Opinions... the salt of IT.
ReplyDeleteFabio,
ReplyDeletedo you honestly use NHibernate.Linq and not hql and criteria ?
@schlachtzeuger
ReplyDeleteMost of the time I'm using HQL with named queries.
In my last DAO I'm using LINQ but for "easy" queries.
In the next prj I'll use QueryOver<T> and LINQ2NH only if I can see some advantage.
Obviously I'm using LINQ2Object a lot.
Hi,
ReplyDeleteHope you are doing very well.
Couple of days ago I posted an article on www.codeproject.com in which I have used Nhibernate.Linq assembly. There are couple of NHibernate.Linq tests failing whilst the LinqToSql test are not. I was wondering if you could have a look to make sure that's not due to my lack of NHibernate knowledge.
The article link is: http://www.codeproject.com/Article.aspx?tag=1556697638653591
We know limitations of actual impl. of NHLQ.
ReplyDeleteFabio,
ReplyDeleteI agree with you that is not good to expose any implementation, as ICriteria. But write a generic query object is a complex task.
Maybe an intermediate solution could be to have an adhoc query object. It does not resolve all scenarios, only the required by your domain problem (more close to ddd).
For example:
product = productRepository.find(
productCriteria.name.is("shoes").
and.price.between(10, 100)
)
By this way, Fabio-BL can think in OO and fluent style (more interesting for him than RDBM issues).
I dont'know. It just an idea before dinner.