How is Command Query Separation (CQS) implemented when using an ORM

cqrsnhibernateorm

The principle behind the CQS architectural pattern is that you separate your queries and commands into distinct paths. Ideally, your persistence store can be read/write partitioned, but in my case, there is a single, normalized database.

If you are using an ORM (NHibernate in my case), it's clear that the ORM is used when issuing commands. But what about all the various queries you need to run to shape data (DTOs) for user screens, is it common practice to ditch the ORM when doing the Query side of CQS?

Where should I implement my queries and DTO projections? Straight ADO.NET (datareaders, dtos, datatables, stored procs)? Some queries are quite unique and involve a lot of joins to pull everything together. I don't want to denormalize the database for the queries, but I could create views (poor man's denormalization).

Best Solution

I'm assuming by CQS you mean the DDD architectural pattern aka CQRS, not strictly the traditional CQS principle.

I'd still use NHibernate for your read only model. There are many advantages such as future and multi queries, lazy/eager loading etc... that will optimize DB chattiness. Additionally, it will be easier to compose queries with an ORM if the UI allows for the user to essentially change the where clause.

Regarding how to technically handle a read only model, you can mark an entity immutable with NHibernate. You could simply mark all of your read model entities immutable. Also, I don't think you can update projections in NHibernate so that is another option to go for as your read only model (Someone please correct me if I am wrong as I am not 100% sure).

Regarding ugly or impossible NH mappings: NH can map to views and stored procedures, so I think it would be fine to use these when you need to. Views are probably a bit more flexible than stored procedures for a read only scenario because your SQL will still be dynamic. However, If you need read / write for any of these flattened structures I'd map to a store procedure.

Related Question