CQRS – Second step – Simple read model

This post series is driven by my lightning talk about how to introduce CQRS to your project. I thought that would be good to explain this topic further for people who won’t be attending my presentation.

I will write about:

  1. splitting code to commands and queries
  2. introducing simple read model
  3. creating read model synchronously
  4. creating read model asynchronously with SignalR notification

Stay tuned 😉

Recent state of your app

After the previous step, you have refactored plenty of your services into separated queries and commands – applied first step in CQRS pattern. Few of services stayed the same, but they contained only 1 or two methods, so you decided to concentrate on things that matters and the rest leave it is.

Now your application logic is defined in small, highly boundaries objects which are easier to tests and maintain. Unfortunately, it is only a structural change – you haven’t improved the performance of your application. But with such clearly defined responsibilities you are able to find bottlenecks and do something with them.

Your current bottleneck:

You find your first bottleneck – gathering product’s offer currently is taking too much time, freezing user interface and blocking to do anything. Currently, your users don’t apply advanced filterings or querying, but even without them reading data is slow and insufficient. When you look into it you realize that your query handler is trying to get too much data in one call which results with enormous SQL query.

With such state of the database (diagram / code) such querying can take a while. Of course, you can split your query and do multiple requests to the database but it only hides the problem but doesn’t solve it. You realize that you need only a small amount of this information from queried objects, but they are significant to fulfill clients’ needs – we need to show product list with:

  • Category name
  • Main picture
  • Manufacturer name and picture
  • Related products with pictures
  • How many time product has been bought
  • Product’s field values
  • Average review ratings and last 5 reviews
  • 3 best discounts for this product

So you decide to do something different – change your query model and introduce new options that will allow doing more advanced querying.

It would be more difficult to introduce with your previous, service pattern, but with current query-command orientation it is really easy because you are not breaking any other part of the system – you focus only on the current object and refactor to toward better value.

Read models

Different read model is a realization of a simple thought:

You want to change your data differently than you read it.

There are many opportunities to introduce different read model to your app, each with different advantages and disadvantages. You can connect directly to a database and apply SQL querying, create SQL view, use different ORM or apply some changes in current ORM to make it more lightweight and faster.

So at the beginning, you define your read model – objects to be retrieved from the database. Then you decide to implement this model in 2 different approaches:  Dapper – MicroORM and AutoMapper + ProjectTo to find best fitting one:

Dapper – MicroOrm

Dapper is a light ORM created by StackExchange – question and answer platform that we continuously use in our everyday work (StackOverflow FTW). It allows to connect directly to the database and operate on a low level, manipulating with resulted objects and returning multiple different rows.

You define a tailored-made SQL query to get an exact amount of data that you need.

First, you define a temporal product table and put all products into it. Then you write multiple SELECT queries to gather all data in one query – first to get products and then related products, field values, reviews and discounts. Not a single excessive field is returned from the database.

After it, with the power of Dapper, you are parse data to strongly-typed classes and combine related objects with parent products.

Advantages:

  • Almost that fast as SQL
  • Returning multiple datasets in one query
  • Built-in mapping

Disadvantages:

  • Mixing SQL  and C#
  • Harder to maintain

AutoMapper + ProjectTo

AutoMapper is a library which allows creating a mapping from / to classes and handles this whole logic in one mapper. It was created by, already mentioned, Jimmy Bogard.

ProjectTo is an extension to this library. It allows you to define mappings from database entities directly to view models. Then you query your database but, at the end, you map (project) your response to a defined view model.

What is really worth mentioning, is that with ProjectTo you can do the stuff, which you can typically do in normal mappings:

  • Mapping arrays
  • Aggregating (sum / average / max / min)
  • Getting first or last
  • Flattening objects

So you create a map which defines your expectations. Because of conventions (Category.Name will be automatically transferred into CategoryName) this map is not so hard to read and maintain.

After that your simplify query handler because you don’t need all these includes – you use ProjectTo to map SQL data to your ProductVm.

Advantages:

  • Default conventions
  • Ease of object / array mapping
  • Aggregate functions
  • Great association with Entity Framework
  • Everything is done in C# code

Disadvantages:

  • No possibility for database tweaks
  • Slower than other more database-centric solutions (but still fast)

Comparing Dapper and AutoMapper + ProjectTo

Both of these options are worth trying and implement in your project.

AutoMapper / ProjectTo is easier it approach; you don’t need any unknown syntax or behaviors. It is a broadly known Entity Framework, only extended, “on steroids”. Moreover, you don’t need to be familiar with SQL syntax and querying – under the hood, it just works.

Dapper is more powerful and closer to a database – you can optimize your queries on a lower level and gain additional performance.

Summary

The second step to implement CQRS in your project is pretty straightforward – focus on most annoying bottlenecks of your application and improve its performance through applying a different approach to read model. With splitting services to command and queries, there is no need to apply a new query model to a whole application. You can try with vary of frameworks to find most fitting to in your context – SQL Views, MicroORMs, AutoMapper + ProjectTo and others. Implement them, measure if you gained value, and then spread to other parts of your system.

This approach has its own limitation – with adequately complicated database structure, even low-level querying in SQL view can be not sufficient. But in a great amount of cases, you can boost the effectiveness of your application with no difficulties.

For these other, more complicated cases, stay tuned for next posts 😉