Skip to content

Transitioning to Event Sourcing, part 2: go CQRS with DTOs

by Julien on July 15th, 2010

Une version française de ce billet est disponible ici.

Overview

In this refactoring, we want to go CQRS. CQRS simply states that queries (that interrogate your system to display information to your users) and commands (that modifies the state of your system) are using different components in your system. This is a good practice at all levels: method level, class level, but also component level or even data level. In this refactoring, we will apply it to the component level, so we don’t use the entities and NHibernate to access our data:

Instead of displaying entities, you are displaying Data Transfer Objects that are custom crafted for the view. Concretely, you are trying to get all the information needed for the view in one single request to the database, doing a custom SQL query.

What? A SQL query? But did not the ORM free ourselves from those beasts from the middle age? Well, yes they did, but at a price. A price we don’t want to pay at this point anymore.

So what is the problem with a HQL or a Linq query against our entities? First of all, our entities are optimized for treating the use cases of our system. The trouble is most of our screens are usually displaying data from more that one entity. For example, if you are displaying a student, you want in that same screen to display the names of the classes he registered in. It means that NHibernate needs to lazy load the associated classes. If you don’t care, you can end up with a lot of round trips to the database. Along the way, you will also load a lot of things you don’t need (for example, all the other properties of the registered classes for that student). Sure, NHibernate supports fetching strategies. It means that for each requests, you can specify what to load right now, and what not. That is additional complexity to your requests and your infrastructure. Then, you have to do a certain amount of transformation in the view to format the data correctly. But the biggest effect is that your entities need to support the view. So instead on focusing on implementing the business rules, the domain must also support display needs. And it turn out that it is most of the time impossible to have the same model optimized both for transactional behavior and display. The main reason is that often times, views are showing a denormalized model, with lot of data duplication. Which you want to avoid at all cost for your domain model.

So ideally, you would have somewhere your DTOs for each view stored exactly as they should be displayed. But we are not there yet, so we will cheat a little bit and do SQL queries for retrieving our DTOs. For example:

public ViewResult Details(Guid studentId)
{
    return View(studentQueries.ById(studentId));
}

At first glance, nothing changed. Except we are not using a repository anymore to perform the query, but a component that is dedicated to read only queries. Then, the query is not returning the Student entity, but a StudentDTO.

Benefits

Simplicity

No more mapping between your entities and your view model. No more fetching strategies. A plain SQL query for each of your views.

No getters in your domain!

You’ve just freed your domain from the UI burden. So now your aggregates do not need to expose their state through getters anymore. You can now change your entities internals without worrying about the UI.

Implementation

What changed

There is one additional project in the solution for the view model:

This project contains the DTOs and their queries.

The repositories have also disappeared:

Since we don’t do queries anymore on our entities, we don’t need specific implementations for each entity. The base repository is enough.

Speaking about the repository, the IPaginable<T> All() method disappeared from its interface as well.

The NHibernate implementation of the IPaginable<T> has been replaced by a raw SQL implementation (pagination is supported started in SQL Server 2005).

As I am not a fan of SQL queries in the code, so I created a simple infrastructure to read the queries from XML files (more on that later).

Finally, there is an additional component, the DTO mapper that maps the result sets from a IDataReader to the DTO objects.

Creating a DTO

In this project, a DTO is a simple class, with public properties and an empty constructor. For example:

public class ClassDTO
{
    public Guid Id { get; set; }
    [Required]
    [StringLength(255, MinimumLength = 1)]
    public string Name { get; set; }
    [Required]
    [Range(3, 6)]
    public int Credits { get; set; }
}

Since my DTOs can be used directly as a view model, I put some validation in there as well.

Creating a DTO query

Here is a simple query interface:

public interface IClassDTOQueries
{
    IPaginable<ClassDTO> All();
}

Here is how it is implemented:

public class SQLClassDTOQueries : DTOQueries, IClassDTOQueries
{
    public SQLClassDTOQueries(IPersistenceManager pm)
        : base(pm)
    {
    }

    public IPaginable<ClassDTO> All()
    {
        return ByNamedQuery<ClassDTO>("All", null);
    }
}

The ByNamedQuery() is taking 3 arguments:

  • The name of the query (to get from the XML file)
  • The parameters for the query
  • (Optional) The mappings to the collections of the DTO

You can browse the sample application for example of queries using these different features.

Finally, you can define the actual query in an XML file having the same name as the queries implementation:

<?xml version="1.0" encoding="utf-8" ?>
<queries>
<query name="All" defaultSort="Id">
<count>SELECT COUNT(*) FROM Class</count>
<select>
SELECT class.Id,
class.name as Name,
class.credits as Credits
FROM Class class
</select>
</query>
</queries>

For each query that will be paginated, you need to actually specify 2 queries:

  • The count query that is returning the total number of items.
  • The select query that is returning the actual items.

For queries that will not be paginated, you can omit the count query.

The DTO mapper will use the column aliases to find the DTO property in which to store the value. The DTO mapper supports simple properties, sub DTOs, and most collections (including dictionaries).

Running the sample

The sources for the entire serie is available on GitHub at http://github.com/jletroui/TransitioningToEventSourcing
.

To run it, simply create a new “DDDPart2″ database in SQLExpress.

Transitioning to Event Sourcing posts:

From → Event Sourcing

15 Comments
  1. HI Julien

    Great series, I have one small question. Why did you decide to use your own rolled dto mapper. Did you have a reason to not use Automapper or something similar

  2. Julien permalink

    Thanks for your comment. I am not a great fan of reinventing the wheel. I search for serveral options before rewriting a mapper. NHibernate transformers, IBatis, Automapper. But all had problems, due to the fact they have not been designed and optimized for this particular use. Some are not supporting collections, some are very verbose and do not support mapping by convention, etc.. Given this is a fairly small amount of code, I preferred do my own instead of spending more time adapting existing mappers to my needs.

  3. James permalink

    If your Aggregates don’t utilize getters, how do you unit test your domain (for instance, when asserting before/after state)?

  4. Julien permalink

    @James

    A solution could be to keep assembly private getters purely for testing purposes. It is then possible to specify that the test assembly has access to the domain assembly private members in the Assembly.cs file. After step 4, the tests can rely on the stronger events instead.

  5. Dypsok permalink

    Bonjour,
    vous indiquer dans cette article que vos dto pour l’affichage sont idéalement “chargés avec une seule requête à la base de données”
    mais les sources sur github montrent que jusqu’au bout de l’exercice plusieurs requêtes sont requises (ce qui me semble tout à fait normal cela dit) , comme par exemple pour afficher la vue de détail d’un étudiant et qui plus est en continuant d’utiliser des jointures…
    Voici les requêtes auxquelles je fais mention :

    SELECT student.Id,
    student.firstName AS FirstName,
    student.lastName AS LastName,
    student.hasGraduated AS HasGraduated
    FROM Student student
    WHERE student.Id = @Id;

    SELECT reg.Id,
    class.Id AS [Class.Id],
    class.name AS [Class.Name],
    class.credits AS [Class.Credits]
    FROM Registration reg
    INNER JOIN Class class ON class.Id = reg.classId
    WHERE aggregateRoot = @Id;

    Dans ce sens il me semble que même avec une approche CRUD on peut avoir le même comportement… Utiliser les mêmes requêtes pour le même résultat… La dénormalisation elle même sous forme de vues SQL ou bien calculée sur chaque changement appliqué à l’entité est possible avec un modèle CRUD classique… Du coup je ne suis pas sûr que les arguments avancés en faveur d’une approche CQRS/ES soient pertinents dans ce cas!
    J’aimerais avoir votre avis sur la question.

  6. Julien permalink

    Merci pour votre commentaire! Vous soulevez un bon point. Le “view model” de l’exemple est juste un point de départ. Grâce au event sourcing, vous pouvez maintenant le dé-normaliser, et avoir une table par vue, là ou c’est nécessaire pour des raisons de performance par exemple. C’est uniquement parce que vous avez une séparation entre la partie transactionnelle de l’application (les agrégats) et la partie lecture que vous avez la liberté de dé-normaliser votre “view model”. Vous n’avez pas cette liberté avec un modèle CRUD.

Trackbacks & Pingbacks

  1. Transitioning to Event Sourcing, part 1: the DDD “light” application | Julien's blog
  2. Tweets that mention Transitioning to Event Sourcing, part 2: go CQRS with DTOs | Julien's blog -- Topsy.com
  3. Transitioning your DDD “light” application to CQRS and Event Sourcing | Julien's blog
  4. Transitioning to Event Sourcing, part 4: track state changes | Julien's blog
  5. Transitioning to Event Sourcing, part 3: commands | Julien's blog
  6. Transitioning to Event Sourcing, part 5: use events for updating your domain database | Julien's blog
  7. Transitioning to Event Sourcing, part 6: store events | Julien's blog
  8. Transitioning to Event Sourcing, part 7: build a view model | Julien's blog
  9. Transitioning to Event Sourcing, part 8: remove the domain database, go event sourced | Julien's blog

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS