Back on Repositories and Paging. Introducing reporting.
By Jérémie Chassaing on Wednesday, April 8, 2009, 14:10 - Domain Driven Design - Permalink
I had quite a lot of comments on my previous post on repositories and paging, and there’s a lot of people coming from google looking for answers… It’s sign that there’s no good response to this issue now.
The problem was how paging (a presentation concern) fits into a repository that should have only domain concerns…
And the short answers is… It doesn’t fit !
It doesn’t mean you should forget everything I told you before but there’s a vision change.
There’s something missing in DDD book…
but present in current discussion about DDD. The book provides good building blocks (entities, repository…) but Evans says himself it has been a bit over emphasized. The book is mainly about maintaining and mutating your domain state, not about it’s presentation.
CQS to the rescue
The CQS (Command Query Separation) principle proposes to decouple Commands that change the state of the system, from Queries that ask the state of the system.
The repository lies on the Command side, that’s why :
- It should be specialized to express only mutations that are possible for the system
- It should not expose presentation concerns like paging
But what’s on the Query side ?
The query side should be far more supple and provide tools to query the state of the domain. It can even be implemented with IQueryable interfaces to provide that flexibility. Asking for pages in this area is natural since you just want to query a part of the domain state.
The goal of all this is to report the state of the system. So Reporting is a good name here.
You can read Greg Young’s DDDD posts to see where CQS can lead : Write only domain model (Commands) and rich scalable distributed Query mechanisms using messaging.
There’s no clear guidance yet in this field but I’m still investigating. The flexibility of a reporting service in the domain layer is still a huge gain to understand where each concept fits.
Comments
Jérémie,
Thanks for the post. I've been trying to distill and consolidate a lot of the information available on DDDD and event sourcing into my blog, http://jonathan-oliver.blogspot.com... Greg says he's working on a paper that explains in more depth each concept behind DDDD but there isn't an excepted date for publication.
@jonathan> I posted that just before the repository war, and I think it's a very important topic. We should sort it out to provide a true guidance for newcomers...
I'm also following your blog, it's a usefull source to understand the DDDD concepts.
Hi, i follow repository war in ddd yahoo group, Greg and in Ayende blog. In the first place I agree to choose custom repository as a default mechanism in datacess, but it comes to cqs this can be split into two type of repository. A generic one can be useful in command and custom repository said in in query layer. Greg has said about when apply cqs , write only domain model is important and this will suite to CUD (Create/Update/Delete) and all this functionality can be put in generic repository, but when it comes to query , we need a custom repository... thats my 2 cent
@ryzam> Mh I don't follow you on this one.
I use a specific repository on the command part. The goal is to express what kind of manipulation can be done with entities. For instance, accounting lines can only be inserted, never updated or removed. It should be expressed in the domain.
On the other side, querying the domain state often spans on serveral entities, aggregations can occure, sorting, paging, filtering should be simple and effective, and frameworks like Linq or NHibernate can expresse it simply, there is no domain logic in querying. So you can use a generic interface (like IQueryable or NH ISessions) for that, use DTOs instead of Entities and even use anonymous objects if you want.
By the way, this is also what Greg is advocating...
Jeremmie: I agree with you but...CQS doesn't solve this problem. It's the thing of presentation strategy to choose e.g. fetching(paging) strategy. In query model is reasonable to exclude repository, but the paging problem remains. Yes, query model per presenation strategy is one possible solution. The other one is similar to code in your previous post, but it uses decorator pattern. You can make injection into you query model layer or use it in repository layer when you don't like CQS. There is also a simple way, hot to make more generic interface without queryable in constructor. (yes, I still see much space for discussion).
public class QueryResult<T>
{
public QueryResult(IQueryable<T> queryable) { _queryable = queryable; }public QueryResult<T> Slice(int from, int count) { return new QueryResult<T>(_queryable.Skip(from).Take(count).AsQueryable()); }public QueryResult<T> OrderBy<TKey>(Func<T, TKey> query) { return OrderBy(query, SortOrder.Ascending); }public QueryResult<T> OrderBy<TKey>(Func<T, TKey> query, SortOrder order) { IOrderedEnumerable<T> result = order == SortOrder.Ascending ? _queryable.OrderBy(query) : _queryable.OrderByDescending(query);return new QueryResult<T>(result.AsQueryable()); }public int Count() { return _queryable.Count(); }public List<T> ToList() { return _queryable.ToList(); }public static implicit operator List<T>(QueryResult<T> result) { return result.ToList(); }}
USAGE:
public class Person
{
public Person(string firstName, string lastName) { _firstName= firstName; _lastName= lastName; }public string FirstName { get { return _firstName; } } public string LastName { get { return _lastName; } }}
public static QueryResult<Person> QueryModelOrRepositoryMethod()
{
for (int i = 0; i < 100; i++) persons.Add(new Person { FirstName = "Joe" + i, LastName = "Doe" + i });}
List<Person> allPersons = QueryModelOrRepositoryMethod ();
Console.WriteLine("Count: {0}", allPersons.Count);
Console.WriteLine("--");
List<Person> persons = QueryModelOrRepositoryMethod()
foreach (Person person in persons)
Console.WriteLine("--");
QueryResult<Person> result = QueryModelOrRepositoryMethod();
int total = result.Count();
persons = result
foreach (Person person in persons)
Console.WriteLine("{0} {1}", person.FirstName, person.LastName);Console.WriteLine("{0} records of total {1}", persons.Count, total);
Add all required capabilities to query result object ...
@T> Yes, on the query part, you can use whatever pattern you want, it just have to return DTO and be flexible enough for your querying purpose.
No business logic should occure when querying.
@jeremie: sure DTO ... yes, your right CQS, solves the problem in specific way - flexibility means, that it's simplier to create query model per each specific presentation layer with specific capabilities/strategy.
But the code above is solution to the repository problem, better as the code you suggested here:
http://thinkbeforecoding.com/post/2...
You can also use this approach when buidling query model when you decide for CQS but you don't want to change query model interfaces(or create new one from the scratch) when new type of presentation layer with specific strategy comes. The code above is an option not an argument against CQS.
@T> If you go CQS, you should not need paging on your repositories, it makes thing simple on the repository side (the Command side).
On the Query part, it's up to you !
Jeremie, please, I don't need to explain what CQS is and how to apply it into DDD - yes, sure i won't need paging in my command model. "On the Query part, it's up to you" - yes, i wrote it already twice before. I hoped, that the code above will be inspiration or subject of discussion, it continues where you ended with previous repository/paging post. It solves chaining and is able to hide observed object like IQueryable. The code is usable also when CQS is used in query model. But very sorry for bothering you with this! If I wanted to read Greg Young's tougths again I'd rather read his blog. Have a nice evening.
@T> Sorry for the misunderstanding... no bothering
Yes the code above is a good way to hide IQueryable details and can be adjusted to Linq providers implementations. And a nice wrapper around IQueryable like the one you proposed for paging purpose gives a more straightforward code on the client side.
It would also be possible to suggest to split the different part for chaining :
IQueryResult<T> { int Count { get; } IOrderedResult<T> OrderBy<TKey>(Func<T, TKey> selector); } IOrderedResult<T> { IOrderedResult<T> ThenBy<TKey>(Func<T, TKey> selector); IEnumerable<T> Slice(int start, int count); }This way, you cannot get an enumerator before ordering the item set. It would be usefull to use an interface to really decouple the user from the IQueryable implementation.
And no need to call count after ordering, and no need to reorder after slicing.
It narrows the chaining to permit only what have a use case meaning.
Hope I didn't upset you with my first answers :-/