Brownfield CQRS part 3 – Queries, Parameters and Results

In the previous two posts, I showed some simple patterns for commands and command handlers. Now let’s talk about the other half of the story: queries!

On our WCF service, each query method:

  • Returns one or more QueryResult objects — a DTO created exclusively for this query.
  • Takes a QueryParameter argument — if required, a DTO containing search criteria, paging options etc.

For example, to query for bookings:

[ServiceContract]
public interface IBookingService
{
    [OperationContract]
    IEnumerable<BookingQueryResult> SearchBookings(
        BookingSearchParameters parameters);
}

Query Parameters

Queries take simple DTO parameter objects just like commands. They carry both search criteria (what to look for) and things like paging options (how to return results). They can also define defaults. For example:

[DataContract]
public class BookingSearchParameters
{
    public BookingSearchParameters()
    {
        // default values
        NumberOfResultsPerPage = 10;
        Page = 1;
    }

    [DataMember]
    public Tuple<DateTime, DateTime> Period { get; set; }

    [DataMember]
    public int NumberOfResultsPerPage { get; set; }

    [DataMember]
    public int PageNumber { get; set; }
}

Query Object

Queries are then executed by a query object — an application service that queries your ORM, reporting store, or domain + automapper (if you’re still using a single model internally for commands and queries).

public interface IQuery<TParameters, TResult>
{
    public TResult Execute(TParameters parameters);
}

Query Results

Queries can return a single result (e.g. to look up the details of a specify item), or a sequence (searching):

public class BookingSearchQuery : 
    IQuery<BookingSearchParameters, IEnumerable<BookingSearchResult>>
{
    public IEnumerable<BookingSearchResult> Execute(
        BookingSearchParameters parameters)
    {
        ...
    }
}

Query results are simple DTOs that provide all the information the client needs.

[DataContract]
public class BookingSearchResult
{
    [DataMember]
    public string PartyName { get; set; }

    [DataMember]
    public int PartySize { get; set; }

    [DataMember]
    public DateTime TimeAndDay { get; set; }

    [DataMember]
    public string SpecialRequests { get; set; }
}

Query Results should be able to be rendered directly on the UI, in one query. If the they require further mapping, or multiple calls (e.g. to get different aspects of an object) before you can use it on a view model, then they are most likely:

  • Too granular — query results should be big flattened/denormalized objects which contain everything you need in one hit.
  • Based on the wrong model (the domain or persistence model) — they should be based on the UI’s needs, and present a screenful of information per call.

As with commands, having a one-to-one mapping between query objects and queries makes it easy to add/remove functionality to a system.

Can’t I use the same object for sending commands and returning results?

That BookingSearchResult looks more or less identical to BookTableCommand we sent before — it has all the same properties. That doesn’t seem very DRY! Can’t I just create a generic DTO and use that in both cases?

Using the same class for commands and queries is called CRUD, and it leads to exactly the sort of situations we are trying to avoid — where commands needs to use different representations of objects than when querying, but can’t because they are tightly coupled to share the same object. As I said in part 1, commands are driven by business transactions, but queries are driven by UX needs, and often include projections, flattening and aggregation — more than just 1:1 mapping.

Next: Part 4 – Command Dispatcher

11 thoughts on “Brownfield CQRS part 3 – Queries, Parameters and Results

  1. I have found WCF Data Services (a.k.a. OData Services) to be invaluable in case of querying the viewmodel. There is virtually no effort required, truly a technology that fits the bill. No ServiceContracts, nor datacontracts, nor tedious mapping code between some intermediate model or plain old ado.net data reader. It’s just fine to use ADO.NET EF in this case. Paging, sorting, filtering are all built-in and you can use LINQ for query type-safety.

    On the client I tend to use a ViewModel (what was queried plus what the user entered) to Command mapper. Again, you can use existing solutions such as Automapper for that purpose. For command validation I use FluentValidation, before shipping off the command to the server.

  2. Hi Richard,
    I noticed that both your query service method and command method (in part 1) use the same service contract interface, IBookingService.

    Am I missing something or should the Commands and Query methods be on 2 separate service contracts to keep a clear segregation between them? Or is this not necessary and having them live in the one WCF service is fine?

  3. Simon: I was hoping no one would mention that :)

    I’m not so confident about whether to use one combined or two separate command/query service endpoints at this stage. Have gone with one for now, for simplicity and simplicity for clients (they shouldn’t care if we’re using CQRS internally). Interested to hear other opinions however.

  4. Hi Rachard,

    One or two WCF service is a tough decision.
    You had already list the advantage for one WCF service.

    But since the repositorys for C and Q are diff,so if I need to have two repositorys in this WCF service,CommandRepository and QueryRepository.

    Maybe you can decide it in runtime,but if you ask IOC to create the Repository,two Repositorys are necessary.

    My english is poor,forgive me!

  5. And an other question.If we use WCF,we can’t use ORM(NH) to fetch QueryResult, NH entity is hard to transfer.

    How to handle it?

  6. @Fred: with CQRS you would likely have a persistent view model/read model containing big flattened DTO objects that contain everything about those objects a client might need.

  7. Hey,Rechard,the flattened DTO objects?Do you mean the object that don’t have dependency but simple properties?

  8. Great series, Richard. I do have a simple question on this Query model thing. Are you creating Query objects for every single Query against the underlying store, or do you have simple data access objects for simpler queries?

    For example if I want to Query for all countries because the user needs to be able to select between them. Would you have a method with the signature:
    IEnumerable ICountryDao.GetAllCountries()

    The alternative that you’re suggesting in this post would be to create a AllCountriesQuery object, implementing
    IEnumerable Execute().

    It does seem like an awful lot of Query objects for a large application, that might become very confusing and grow immensely over time. What do you think? Should we use data access objects for simpler queries against the underlying store for simple queries that takes none or few parameters?

Comments are closed.